create-yss-spec 2.2.10 → 3.0.0
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 +1 -1
- package/package.json +1 -1
- package/template/.agents/skills/ask-matt/SKILL.md +1 -1
- package/template/.agents/skills/wayfinder/SKILL.md +2 -2
- package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.agents/skills/yss-domain/SKILL.md +2 -1
- package/template/.agents/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.agents/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.agents/skills/yss-dto/SKILL.md +6 -0
- package/template/.agents/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.agents/skills/yss-repository/SKILL.md +15 -13
- package/template/.agents/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.agents/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.agents/skills/yss-research/SKILL.md +73 -0
- package/template/.agents/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.agents/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.agents/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.agents/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.agents/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.agents/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.agents/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.agents/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.agents/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.agents/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.agents/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.agents/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.agents/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.agents/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.agents/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.agents/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.claude/skills/ask-matt/SKILL.md +1 -1
- package/template/.claude/skills/wayfinder/SKILL.md +2 -2
- package/template/.claude/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.claude/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.claude/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.claude/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.claude/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.claude/skills/yss-domain/SKILL.md +2 -1
- package/template/.claude/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.claude/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.claude/skills/yss-dto/SKILL.md +6 -0
- package/template/.claude/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.claude/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.claude/skills/yss-repository/SKILL.md +15 -13
- package/template/.claude/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.claude/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.claude/skills/yss-research/SKILL.md +73 -0
- package/template/.claude/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.claude/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.claude/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.claude/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.claude/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.claude/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.claude/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.claude/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.claude/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.claude/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.claude/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.claude/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.claude/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.claude/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.claude/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.claude/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.claude/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.codex/skills/ask-matt/SKILL.md +1 -1
- package/template/.codex/skills/product-design/package.json +4 -2
- package/template/.codex/skills/product-design/skills/research/SKILL.md +22 -0
- package/template/.codex/skills/product-design/skills/research/assets/evidence-template.yaml +65 -0
- package/template/.codex/skills/product-design/skills/research/assets/research-brief-template.md +33 -0
- package/template/.codex/skills/product-design/skills/research/references/evidence-audited.md +80 -0
- package/template/.codex/skills/product-design/skills/research/scripts/validate-research-package.mjs +149 -0
- package/template/.codex/skills/product-design/skills/research/tests/run-scenarios.mjs +72 -0
- package/template/.codex/skills/wayfinder/SKILL.md +2 -2
- package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.codex/skills/yss-domain/SKILL.md +2 -1
- package/template/.codex/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.codex/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.codex/skills/yss-dto/SKILL.md +6 -0
- package/template/.codex/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.codex/skills/yss-repository/SKILL.md +15 -13
- package/template/.codex/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.codex/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.codex/skills/yss-research/SKILL.md +73 -0
- package/template/.codex/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.codex/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.codex/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.codex/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.codex/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.codex/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.codex/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.codex/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.codex/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.codex/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.codex/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.codex/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.codex/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.codex/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.codex/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.codex/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.cursor/skills/ask-matt/SKILL.md +1 -1
- package/template/.cursor/skills/wayfinder/SKILL.md +2 -2
- package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.cursor/skills/yss-domain/SKILL.md +2 -1
- package/template/.cursor/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.cursor/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.cursor/skills/yss-dto/SKILL.md +6 -0
- package/template/.cursor/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.cursor/skills/yss-repository/SKILL.md +15 -13
- package/template/.cursor/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.cursor/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.cursor/skills/yss-research/SKILL.md +73 -0
- package/template/.cursor/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.cursor/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.cursor/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.cursor/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.cursor/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.cursor/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.cursor/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.cursor/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.cursor/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.cursor/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.cursor/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.cursor/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.cursor/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.cursor/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.hermes/skills/ask-matt/SKILL.md +1 -1
- package/template/.hermes/skills/wayfinder/SKILL.md +2 -2
- package/template/.hermes/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.hermes/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.hermes/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.hermes/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.hermes/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.hermes/skills/yss-domain/SKILL.md +2 -1
- package/template/.hermes/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.hermes/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.hermes/skills/yss-dto/SKILL.md +6 -0
- package/template/.hermes/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.hermes/skills/yss-repository/SKILL.md +15 -13
- package/template/.hermes/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.hermes/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.hermes/skills/yss-research/SKILL.md +73 -0
- package/template/.hermes/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.hermes/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.hermes/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.hermes/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.hermes/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.hermes/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.hermes/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.hermes/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.hermes/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.hermes/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.hermes/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.hermes/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.hermes/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.hermes/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.hermes/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.hermes/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.pi/skills/ask-matt/SKILL.md +1 -1
- package/template/.pi/skills/wayfinder/SKILL.md +2 -2
- package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.pi/skills/yss-domain/SKILL.md +2 -1
- package/template/.pi/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.pi/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.pi/skills/yss-dto/SKILL.md +6 -0
- package/template/.pi/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.pi/skills/yss-repository/SKILL.md +15 -13
- package/template/.pi/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.pi/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.pi/skills/yss-research/SKILL.md +73 -0
- package/template/.pi/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.pi/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.pi/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.pi/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.pi/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.pi/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.pi/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.pi/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.pi/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.pi/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.pi/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.pi/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.pi/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.pi/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.pi/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.pi/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.qoder/skills/ask-matt/SKILL.md +1 -1
- package/template/.qoder/skills/wayfinder/SKILL.md +2 -2
- package/template/.qoder/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.qoder/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.qoder/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.qoder/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.qoder/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.qoder/skills/yss-domain/SKILL.md +2 -1
- package/template/.qoder/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.qoder/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.qoder/skills/yss-dto/SKILL.md +6 -0
- package/template/.qoder/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.qoder/skills/yss-repository/SKILL.md +15 -13
- package/template/.qoder/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.qoder/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.qoder/skills/yss-research/SKILL.md +73 -0
- package/template/.qoder/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.qoder/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.qoder/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.qoder/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.qoder/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.qoder/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.qoder/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.qoder/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.qoder/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.qoder/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.qoder/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.qoder/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.qoder/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.qoder/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.qoder/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.qoder/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/.trae/skills/ask-matt/SKILL.md +1 -1
- package/template/.trae/skills/wayfinder/SKILL.md +2 -2
- package/template/.trae/skills/yss-ddd-scaffold-generator/SKILL.md +14 -3
- package/template/.trae/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +3 -3
- package/template/.trae/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +12 -1
- package/template/.trae/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +2 -1
- package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +45 -11
- package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +164 -0
- package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +18 -4
- package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +172 -0
- package/template/.trae/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +14 -1
- package/template/.trae/skills/yss-domain/SKILL.md +2 -1
- package/template/.trae/skills/yss-domain/agents/openai.yaml +2 -2
- package/template/.trae/skills/yss-domain/references/domain-layer-guide.md +2 -0
- package/template/.trae/skills/yss-dto/SKILL.md +6 -0
- package/template/.trae/skills/yss-dto/references/openapi-wire-profile.yaml +6 -0
- package/template/.trae/skills/yss-product-lifecycle/SKILL.md +1 -1
- 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 +12 -10
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +2 -2
- package/template/.trae/skills/yss-repository/SKILL.md +15 -13
- package/template/.trae/skills/yss-repository/agents/openai.yaml +2 -2
- package/template/.trae/skills/yss-repository/references/infrastructure-layer-guide.md +1 -1
- package/template/.trae/skills/yss-research/SKILL.md +73 -0
- package/template/.trae/skills/yss-research/agents/openai.yaml +3 -0
- package/template/.trae/skills/yss-research/assets/evidence-template.yaml +68 -0
- package/template/.trae/skills/yss-research/assets/research-brief-template.md +36 -0
- package/template/.trae/skills/yss-research/references/evidence-contract.md +42 -0
- package/template/.trae/skills/yss-research/scripts/validate-research-package.mjs +128 -0
- package/template/.trae/skills/yss-research/tests/run-scenarios.mjs +81 -0
- package/template/.trae/skills/yss-router/references/router-contract.yaml +7 -3
- package/template/.trae/skills/yss-stage-decision/SKILL.md +2 -1
- package/template/.trae/skills/yss-web-controller/SKILL.md +16 -10
- package/template/.trae/skills/yss-web-controller/agents/openai.yaml +2 -2
- package/template/.trae/skills/yss-web-controller/assets/templates/Controller.java.template +11 -11
- package/template/.trae/skills/yss-web-controller/assets/templates/WebConvertor.java.template +4 -7
- package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/CreateRequest.java.template +2 -2
- package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/PageRequest.java.template +4 -7
- package/template/.trae/skills/yss-web-controller/assets/templates/web/dto/UpdateRequest.java.template +2 -2
- package/template/.trae/skills/yss-web-controller/references/web-adapter-layer-guide.md +6 -4
- package/template/.trae/skills/yss-web-controller/references/web-generation-contract.schema.json +97 -0
- package/template/.trae/skills/yss-web-controller/scripts/generate_controller.mjs +141 -20
- package/template/.trae/skills/yss-web-controller/scripts/generate_controller.test.mjs +131 -35
- package/template/AGENTS.md +2 -2
- package/template/CONTEXT.md +1 -1
- package/template/docs/agents/digital-human-roles.yaml +2 -2
- package/template/docs/agents/yss-skill-registry.yaml +18 -3
- package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +1 -0
- package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
- package/template/scripts/lib/scenario-checks.mjs +8 -4
- package/template/scripts/lib/skill-registry.mjs +49 -5
- package/template/scripts/lib/skill-supply-chain.mjs +3 -2
- package/template/scripts/verify-yss-dto-openapi-profile +8 -0
- package/template/skills-lock.json +30 -30
- package/template.snapshot.json +4 -4
- package/template/.agents/skills/research/SKILL.md +0 -12
- package/template/.agents/skills/research/agents/openai.yaml +0 -3
- package/template/.claude/skills/research/SKILL.md +0 -12
- package/template/.claude/skills/research/agents/openai.yaml +0 -3
- package/template/.codex/skills/research/SKILL.md +0 -12
- package/template/.codex/skills/research/agents/openai.yaml +0 -3
- package/template/.cursor/skills/research/SKILL.md +0 -12
- package/template/.cursor/skills/research/agents/openai.yaml +0 -3
- package/template/.hermes/skills/research/SKILL.md +0 -12
- package/template/.hermes/skills/research/agents/openai.yaml +0 -3
- package/template/.pi/skills/research/SKILL.md +0 -12
- package/template/.pi/skills/research/agents/openai.yaml +0 -3
- package/template/.qoder/skills/research/SKILL.md +0 -12
- package/template/.qoder/skills/research/agents/openai.yaml +0 -3
- package/template/.trae/skills/research/SKILL.md +0 -12
- package/template/.trae/skills/research/agents/openai.yaml +0 -3
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import { chmod, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
import path from "node:path";
|
|
@@ -7,9 +8,11 @@ import test from "node:test";
|
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
|
|
9
10
|
import { generate as generateWeb, parseArgs as parseWebArgs } from "../../yss-web-controller/scripts/generate_controller.mjs";
|
|
11
|
+
import { runFirstSliceVerification } from "./run_first_slice_verification.mjs";
|
|
10
12
|
|
|
11
13
|
const scripts = path.dirname(fileURLToPath(import.meta.url));
|
|
12
14
|
const generator = path.join(scripts, "generate_scaffold.mjs");
|
|
15
|
+
const dtoWireProfileFile = path.resolve(scripts, "../../yss-dto/references/openapi-wire-profile.yaml");
|
|
13
16
|
const execute = (file, args, options = {}) => new Promise((resolve) => execFile(file, args, { encoding: "utf8", ...options }, (error, stdout, stderr) => resolve({ code: error?.code ?? 0, stdout, stderr })));
|
|
14
17
|
|
|
15
18
|
function scaffoldContract(output) {
|
|
@@ -57,12 +60,19 @@ async function prepareGoldenProject(t) {
|
|
|
57
60
|
const generated = await execute(process.execPath, [generator, ...args]);
|
|
58
61
|
assert.equal(generated.code, 0, generated.stderr);
|
|
59
62
|
const project = path.join(output, "golden-service");
|
|
63
|
+
const manifestPath = path.join(project, ".yss", "scaffold-generation.json");
|
|
64
|
+
const emptyVerificationPath = path.join(root, "empty-scaffold-verification.json");
|
|
65
|
+
await writeFile(emptyVerificationPath, JSON.stringify({ status: "passed", completion_level: "empty-scaffold-verified", commands: ["./mvnw validate", "./mvnw test", "./mvnw package"] }));
|
|
66
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
67
|
+
manifest.completion_level = "empty-scaffold-verified";
|
|
68
|
+
manifest.empty_scaffold_verification_ref = emptyVerificationPath;
|
|
69
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
60
70
|
const java = "src/main/java/com/yss/golden";
|
|
61
71
|
const testJava = "src/test/java/com/yss/golden";
|
|
62
72
|
await writeJava(project, `golden-service-domain/${java}/domain/quality/model/QualityRule.java`, `package com.yss.golden.domain.quality.model;
|
|
63
73
|
public final class QualityRule { private final Long id; private final String name; public QualityRule(Long id, String name) { if (name == null || name.trim().isEmpty()) throw new IllegalArgumentException("quality-rule-name-required"); this.id=id; this.name=name; } public Long getId(){return id;} public String getName(){return name;} }`);
|
|
64
74
|
await writeJava(project, `golden-service-domain/${java}/domain/quality/gateway/QualityRuleGateway.java`, `package com.yss.golden.domain.quality.gateway;
|
|
65
|
-
import com.yss.golden.domain.quality.model.QualityRule; public interface QualityRuleGateway { void save(QualityRule rule); }`);
|
|
75
|
+
import com.yss.golden.domain.quality.model.QualityRule; import java.util.Optional; public interface QualityRuleGateway { Optional<QualityRule> findById(Long id); void save(QualityRule rule); void delete(Long id); }`);
|
|
66
76
|
await writeJava(project, `golden-service-domain/${testJava}/domain/quality/model/QualityRuleTest.java`, `package com.yss.golden.domain.quality.model;
|
|
67
77
|
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; class QualityRuleTest { @Test void rejectsBlankName(){ assertThrows(IllegalArgumentException.class, () -> new QualityRule(1L, " ")); } }`);
|
|
68
78
|
for (const [relative, source] of Object.entries({
|
|
@@ -72,16 +82,26 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertion
|
|
|
72
82
|
[`golden-service-application/${java}/application/result/QualityRuleResult.java`]: `package com.yss.golden.application.result; import java.io.Serializable; public class QualityRuleResult implements Serializable { private Long id; private String ruleName; public Long getId(){return id;} public void setId(Long value){this.id=value;} public String getRuleName(){return ruleName;} public void setRuleName(String value){this.ruleName=value;} }`,
|
|
73
83
|
[`golden-service-application/${java}/application/port/QualityRuleQueryPort.java`]: `package com.yss.golden.application.port; import com.yss.cloud.dto.result.PageResult; import com.yss.golden.application.query.QualityRulePageQuery; import com.yss.golden.application.result.QualityRuleResult; public interface QualityRuleQueryPort { PageResult<QualityRuleResult> page(QualityRulePageQuery query); }`,
|
|
74
84
|
[`golden-service-application/${java}/application/service/QualityRuleService.java`]: `package com.yss.golden.application.service; import com.yss.cloud.dto.result.PageResult; import com.yss.golden.application.command.*; import com.yss.golden.application.query.QualityRulePageQuery; import com.yss.golden.application.result.QualityRuleResult; public interface QualityRuleService { PageResult<QualityRuleResult> page(QualityRulePageQuery query); QualityRuleResult detail(Long id); Long add(QualityRuleCreateCommand command); Boolean update(QualityRuleUpdateCommand command); Boolean delete(Long id); }`,
|
|
85
|
+
[`golden-service-application/${java}/application/service/impl/QualityRuleServiceImpl.java`]: `package com.yss.golden.application.service.impl;
|
|
86
|
+
import com.yss.cloud.dto.result.PageResult; import com.yss.golden.application.command.*; import com.yss.golden.application.port.QualityRuleQueryPort; import com.yss.golden.application.query.QualityRulePageQuery; import com.yss.golden.application.result.QualityRuleResult; import com.yss.golden.application.service.QualityRuleService; import com.yss.golden.domain.quality.gateway.QualityRuleGateway; import com.yss.golden.domain.quality.model.QualityRule; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;
|
|
87
|
+
@Service public class QualityRuleServiceImpl implements QualityRuleService { private final QualityRuleGateway gateway; private final QualityRuleQueryPort queryPort; public QualityRuleServiceImpl(QualityRuleGateway gateway, QualityRuleQueryPort queryPort){this.gateway=gateway;this.queryPort=queryPort;} public PageResult<QualityRuleResult> page(QualityRulePageQuery query){return queryPort.page(query);} public QualityRuleResult detail(Long id){QualityRule rule=gateway.findById(id).orElseThrow(IllegalArgumentException::new); QualityRuleResult result=new QualityRuleResult(); result.setId(rule.getId()); result.setRuleName(rule.getName()); return result;} @Transactional public Long add(QualityRuleCreateCommand command){QualityRule rule=new QualityRule(null, command.getRuleName()); gateway.save(rule); return rule.getId();} @Transactional public Boolean update(QualityRuleUpdateCommand command){gateway.save(new QualityRule(command.getId(), command.getRuleName())); return true;} @Transactional public Boolean delete(Long id){gateway.delete(id); return true;} }`,
|
|
75
88
|
[`golden-service-application/${java}/application/error/QualityRuleConflictException.java`]: `package com.yss.golden.application.error; public final class QualityRuleConflictException extends RuntimeException { public QualityRuleConflictException(String message) { super(message); } }`,
|
|
76
89
|
[`golden-service-application/${java}/application/error/QualityRuleSystemException.java`]: `package com.yss.golden.application.error; public final class QualityRuleSystemException extends RuntimeException { public QualityRuleSystemException(String message, Throwable cause) { super(message, cause); } }`,
|
|
77
90
|
[`golden-service-infrastructure/${java}/infrastructure/persistence/po/QualityRulePO.java`]: `package com.yss.golden.infrastructure.persistence.po; public class QualityRulePO { private Long id; private String name; public Long getId(){return id;} public void setId(Long value){this.id=value;} public String getName(){return name;} public void setName(String value){this.name=value;} }`,
|
|
78
|
-
[`golden-service-infrastructure/${java}/infrastructure/persistence/convertor/QualityRulePersistenceConvertor.java`]: `package com.yss.golden.infrastructure.persistence.convertor; import com.yss.golden.domain.quality.model.QualityRule; import com.yss.golden.infrastructure.persistence.po.QualityRulePO; import org.mapstruct.Mapper; @Mapper(componentModel="spring") public interface QualityRulePersistenceConvertor { QualityRule toDomain(QualityRulePO source); QualityRulePO toPO(QualityRule source); }
|
|
91
|
+
[`golden-service-infrastructure/${java}/infrastructure/persistence/convertor/QualityRulePersistenceConvertor.java`]: `package com.yss.golden.infrastructure.persistence.convertor; import com.yss.golden.domain.quality.model.QualityRule; import com.yss.golden.infrastructure.persistence.po.QualityRulePO; import org.mapstruct.Mapper; @Mapper(componentModel="spring") public interface QualityRulePersistenceConvertor { QualityRule toDomain(QualityRulePO source); QualityRulePO toPO(QualityRule source); }`,
|
|
92
|
+
[`golden-service-infrastructure/${java}/infrastructure/persistence/repository/QualityRuleRepository.java`]: `package com.yss.golden.infrastructure.persistence.repository; import com.yss.golden.infrastructure.persistence.po.QualityRulePO; import java.util.List; import org.apache.ibatis.annotations.*; @Mapper public interface QualityRuleRepository { @Select("select id, rule_name as name from quality_rule where id = #{id}") QualityRulePO selectById(Long id); @Select("select id, rule_name as name from quality_rule") List<QualityRulePO> selectAll(); @Insert("insert into quality_rule(rule_name) values(#{name})") int insert(QualityRulePO po); @Delete("delete from quality_rule where id = #{id}") int deleteById(Long id); }`,
|
|
93
|
+
[`golden-service-infrastructure/${java}/infrastructure/persistence/gateway/QualityRuleGatewayImpl.java`]: `package com.yss.golden.infrastructure.persistence.gateway; import com.yss.golden.domain.quality.gateway.QualityRuleGateway; import com.yss.golden.domain.quality.model.QualityRule; import com.yss.golden.infrastructure.persistence.convertor.QualityRulePersistenceConvertor; import com.yss.golden.infrastructure.persistence.repository.QualityRuleRepository; import java.util.Optional; import org.springframework.stereotype.Repository; @Repository public class QualityRuleGatewayImpl implements QualityRuleGateway { private final QualityRuleRepository repository; private final QualityRulePersistenceConvertor convertor; public QualityRuleGatewayImpl(QualityRuleRepository repository, QualityRulePersistenceConvertor convertor){this.repository=repository;this.convertor=convertor;} public Optional<QualityRule> findById(Long id){return Optional.ofNullable(repository.selectById(id)).map(convertor::toDomain);} public void save(QualityRule rule){repository.insert(convertor.toPO(rule));} public void delete(Long id){repository.deleteById(id);} }`,
|
|
94
|
+
[`golden-service-infrastructure/${java}/infrastructure/query/adapter/QualityRuleQueryAdapter.java`]: `package com.yss.golden.infrastructure.query.adapter; import com.yss.cloud.dto.result.PageResult; import com.yss.golden.application.port.QualityRuleQueryPort; import com.yss.golden.application.query.QualityRulePageQuery; import com.yss.golden.application.result.QualityRuleResult; import java.util.Collections; import org.springframework.stereotype.Repository; @Repository public class QualityRuleQueryAdapter implements QualityRuleQueryPort { public PageResult<QualityRuleResult> page(QualityRulePageQuery query){return PageResult.of(Collections.emptyList(), 0L, query.getPageSize(), query.getPageIndex());} }`
|
|
79
95
|
})) await writeJava(project, relative, source);
|
|
96
|
+
await writeJava(project, `golden-service-application/${testJava}/application/service/QualityRuleServiceTest.java`, `package com.yss.golden.application.service; import com.yss.golden.application.command.QualityRuleCreateCommand; import com.yss.golden.application.port.QualityRuleQueryPort; import com.yss.golden.application.service.impl.QualityRuleServiceImpl; import com.yss.golden.domain.quality.gateway.QualityRuleGateway; import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; class QualityRuleServiceTest { @Test void createPersistsThroughDomainGateway(){QualityRuleGateway gateway=mock(QualityRuleGateway.class); QualityRuleCreateCommand command=new QualityRuleCreateCommand(); command.setRuleName("rule"); new QualityRuleServiceImpl(gateway, mock(QualityRuleQueryPort.class)).add(command); verify(gateway).save(any());} }`);
|
|
97
|
+
await writeJava(project, `golden-service-infrastructure/${testJava}/infrastructure/persistence/QualityRuleGatewayIntegrationTest.java`, `package com.yss.golden.infrastructure.persistence; import com.yss.golden.infrastructure.persistence.convertor.QualityRulePersistenceConvertor; import com.yss.golden.infrastructure.persistence.po.QualityRulePO; import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; import static org.junit.jupiter.api.Assertions.assertEquals; class QualityRuleGatewayIntegrationTest { @Test void persistenceMappingRoundTrips(){QualityRulePO po=new QualityRulePO(); po.setId(1L); po.setName("rule"); assertEquals("rule", Mappers.getMapper(QualityRulePersistenceConvertor.class).toDomain(po).getName());} }`);
|
|
80
98
|
const metadata = path.join(root, "metadata.json");
|
|
81
99
|
const webContract = path.join(root, "web-contract.json");
|
|
100
|
+
const dtoWireProfileDigest = createHash("sha256").update(await readFile(dtoWireProfileFile)).digest("hex");
|
|
82
101
|
await writeFile(metadata, JSON.stringify({ tables: [{ table_name: "quality_rule", table_comment: "质量规则", columns: [{ name: "id", sql_type: "bigint", primary: true, nullable: false }, { name: "rule_name", sql_type: "varchar(64)", nullable: false }] }] }));
|
|
83
|
-
|
|
84
|
-
await
|
|
102
|
+
const webProject = path.join(project, "golden-service-adapter", "golden-service-web");
|
|
103
|
+
await writeFile(webContract, JSON.stringify({ schema_version: 2, contract_id: "golden-slice-1", contract_version: 1, current_version: 1, slice_id: "quality-rule-first-slice", status: "approved", integration_mode: "scaffold-v2", implementation_project_root: project, scaffold_manifest_ref: manifestPath, base_package: "com.yss.golden", module_name: "golden", domain_segment: "quality", application_service_package: "com.yss.golden.application.service", architecture_profile: "target-domain-model", platform_profile: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web", dto_wire_profile_ref: dtoWireProfileFile, dto_wire_profile_digest: dtoWireProfileDigest, openapi_freeze_ref: "openapi://quality-rule@1", allowed_write_paths: [path.join(webProject, "src/main/java/com/yss/golden/rest")], expected_evidence_files: ["first-slice-verification.json"], verification_commands: ["./mvnw test", "./mvnw package"], fields: { quality_rule: { create: ["rule_name"], update: ["id", "rule_name"], query: ["rule_name"], pagination: ["pageIndex", "pageSize"], response: ["id", "rule_name"] } } }));
|
|
104
|
+
await generateWeb(parseWebArgs(["--metadata-file", metadata, "--contract-file", webContract, "--dto-wire-profile-file", dtoWireProfileFile, "--scaffold-manifest-file", manifestPath, "--base-package", "com.yss.golden", "--module-name", "golden", "--domain-segment", "quality", "--web-project-dir", webProject, "--application-service-package", "com.yss.golden.application.service", "--validation-namespace", "javax"]), { log() {}, warn() {} });
|
|
85
105
|
await writeJava(project, `golden-service-adapter/golden-service-web/${java}/rest/advice/TargetProfileExceptionAdvice.java`, `package com.yss.golden.rest.advice;
|
|
86
106
|
import com.yss.golden.application.error.QualityRuleConflictException;
|
|
87
107
|
import com.yss.golden.application.error.QualityRuleSystemException;
|
|
@@ -147,7 +167,10 @@ class GoldenExceptionIntegrationTest {
|
|
|
147
167
|
@GetMapping("/exception-probe/unknown") void unknown() { throw new RuntimeException("runtime-secret"); }
|
|
148
168
|
}
|
|
149
169
|
}`);
|
|
150
|
-
|
|
170
|
+
const sliceContract = path.join(root, "slice-contract.json");
|
|
171
|
+
const requiredSkills = ["yss-domain", "yss-application", "yss-repository", "yss-mybatis", "yss-web-controller", "yss-dto", "yss-exception", "yss-validation", "mapstruct", "lombok", "alibaba-java-code-style"];
|
|
172
|
+
await writeFile(sliceContract, JSON.stringify({ schema_version: 1, contract_id: "golden-slice-1", contract_version: 1, slice_id: "quality-rule-first-slice", status: "approved", readiness: { blockers: [], stale_inputs: [] }, common: { required_skills: requiredSkills }, backend: { status: "required", affected_layers: ["domain", "application", "infrastructure", "web"], required_skills: requiredSkills }, work_units: [{ id: "slice-backend", contract_id: "golden-slice-1", contract_version: 1, work_unit: { primary_skill: "yss-domain" } }] }));
|
|
173
|
+
return { root, project, sliceContract };
|
|
151
174
|
}
|
|
152
175
|
|
|
153
176
|
test("golden first slice 组合 Domain/Application/Infrastructure/Web 且不发生 DTO/PO 穿层", async (t) => {
|
|
@@ -158,25 +181,36 @@ test("golden first slice 组合 Domain/Application/Infrastructure/Web 且不发
|
|
|
158
181
|
const controller = await readFile(path.join(project, "golden-service-adapter", "golden-service-web", "src/main/java/com/yss/golden/rest/QualityRuleController.java"), "utf8");
|
|
159
182
|
const convertor = await readFile(path.join(project, "golden-service-adapter", "golden-service-web", "src/main/java/com/yss/golden/rest/convertor/QualityRuleWebConvertor.java"), "utf8");
|
|
160
183
|
const pageRequest = await readFile(path.join(project, "golden-service-adapter", "golden-service-web", "src/main/java/com/yss/golden/rest/dto/request/QualityRulePageRequest.java"), "utf8");
|
|
184
|
+
const serviceImpl = await readFile(path.join(project, "golden-service-application", "src/main/java/com/yss/golden/application/service/impl/QualityRuleServiceImpl.java"), "utf8");
|
|
185
|
+
const repository = await readFile(path.join(project, "golden-service-infrastructure", "src/main/java/com/yss/golden/infrastructure/persistence/repository/QualityRuleRepository.java"), "utf8");
|
|
186
|
+
const gatewayImpl = await readFile(path.join(project, "golden-service-infrastructure", "src/main/java/com/yss/golden/infrastructure/persistence/gateway/QualityRuleGatewayImpl.java"), "utf8");
|
|
187
|
+
const queryAdapter = await readFile(path.join(project, "golden-service-infrastructure", "src/main/java/com/yss/golden/infrastructure/query/adapter/QualityRuleQueryAdapter.java"), "utf8");
|
|
161
188
|
assert.doesNotMatch(domainPom, /yss-component-dto|yss-component-exception|spring-web|swagger/);
|
|
162
189
|
assert.match(applicationPom, /yss-component-dto/);
|
|
163
190
|
assert.doesNotMatch(webPom, /golden-service-domain|golden-service-infrastructure/);
|
|
164
191
|
assert.doesNotMatch(`${controller}\n${convertor}`, /\.domain\.|\.infrastructure\.|\.repository\.|Mappers\.getMapper|INSTANCE/);
|
|
165
192
|
assert.match(convertor, /@Mapper\(componentModel = "spring"\)/);
|
|
166
193
|
assert.doesNotMatch(pageRequest, /offset|needTotalCount|tempTotalCount|extends PageQuery/);
|
|
194
|
+
assert.match(pageRequest, /pageIndex/);
|
|
195
|
+
assert.match(pageRequest, /pageSize/);
|
|
196
|
+
assert.doesNotMatch(pageRequest, /orderBy|orderDirection|groupBy/);
|
|
197
|
+
assert.match(serviceImpl, /implements QualityRuleService/);
|
|
198
|
+
assert.match(serviceImpl, /@Transactional/);
|
|
199
|
+
assert.match(repository, /@Mapper/);
|
|
200
|
+
assert.match(gatewayImpl, /implements QualityRuleGateway/);
|
|
201
|
+
assert.match(queryAdapter, /implements QualityRuleQueryPort/);
|
|
167
202
|
});
|
|
168
203
|
|
|
169
|
-
test("golden first slice 通过真实 Wrapper
|
|
204
|
+
test("golden first slice 通过真实 Wrapper 后才写入 first-slice-verified", { timeout: 300_000 }, async (t) => {
|
|
170
205
|
if (!["YSS_MAVEN_REPOSITORY_URL", "MAVEN_REPO_USERNAME", "MAVEN_REPO_PASSWORD"].every((name) => process.env[name])) {
|
|
171
206
|
t.skip("缺少受控 YSS Maven 仓库环境;不得声明 first-slice-verified");
|
|
172
207
|
return;
|
|
173
208
|
}
|
|
174
|
-
const { project } = await prepareGoldenProject(t);
|
|
209
|
+
const { project, sliceContract, root } = await prepareGoldenProject(t);
|
|
175
210
|
await chmod(path.join(project, "mvnw"), 0o755);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
211
|
+
const result = await runFirstSliceVerification(project, path.join(root, "first-slice-evidence"), sliceContract, process.env);
|
|
212
|
+
assert.equal(result.status, "passed", JSON.stringify(result, null, 2));
|
|
213
|
+
assert.equal(JSON.parse(await readFile(path.join(project, ".yss/scaffold-generation.json"), "utf8")).completion_level, "first-slice-verified");
|
|
180
214
|
});
|
|
181
215
|
|
|
182
216
|
test("真实 YSS exception starter 的自动配置、优先级、三类映射和序列化均受保护", { timeout: 300_000 }, async (t) => {
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { chmod, mkdir, mkdtemp, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
import { runFirstSliceVerification } from "./run_first_slice_verification.mjs";
|
|
10
|
+
|
|
11
|
+
const requiredSkills = ["yss-domain", "yss-application", "yss-repository", "yss-mybatis", "yss-web-controller", "yss-dto", "yss-exception", "yss-validation", "mapstruct", "lombok", "alibaba-java-code-style"];
|
|
12
|
+
const scripts = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const repositoryRoot = path.resolve(scripts, "../../../..");
|
|
14
|
+
|
|
15
|
+
async function treeDigest(root) {
|
|
16
|
+
const relativeFiles = [];
|
|
17
|
+
async function visit(directory) {
|
|
18
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
19
|
+
const absolute = path.join(directory, entry.name);
|
|
20
|
+
if (entry.isDirectory()) await visit(absolute);
|
|
21
|
+
else if (entry.isFile()) relativeFiles.push(path.relative(root, absolute).split(path.sep).join("/"));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
await visit(root);
|
|
25
|
+
const hash = createHash("sha256");
|
|
26
|
+
for (const relative of relativeFiles.sort()) hash.update(relative).update("\0").update(await readFile(path.join(root, relative))).update("\0");
|
|
27
|
+
return hash.digest("hex");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function readiness() {
|
|
31
|
+
const downstreamSkills = {};
|
|
32
|
+
for (const skill of requiredSkills) downstreamSkills[skill] = await treeDigest(path.join(repositoryRoot, ".agents", "skills", skill));
|
|
33
|
+
const digest = async (relative) => createHash("sha256").update(await readFile(path.join(repositoryRoot, relative))).digest("hex");
|
|
34
|
+
return {
|
|
35
|
+
downstream_skills: downstreamSkills,
|
|
36
|
+
contracts: {
|
|
37
|
+
scaffold_parent: await digest(".agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md"),
|
|
38
|
+
router_contract: await digest(".agents/skills/yss-router/references/router-contract.yaml")
|
|
39
|
+
},
|
|
40
|
+
architecture_ruleset: "b".repeat(64)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function write(project, relative, content = "fixture\n") {
|
|
45
|
+
const target = path.join(project, relative);
|
|
46
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
47
|
+
await writeFile(target, content);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function fixture({ omitGatewayImpl = false, omitReadiness = false } = {}) {
|
|
51
|
+
const root = await mkdtemp(path.join(os.tmpdir(), "yss-first-slice-verification-"));
|
|
52
|
+
const project = path.join(root, "demo-service");
|
|
53
|
+
const evidence = path.join(root, "evidence");
|
|
54
|
+
const manifestPath = path.join(project, ".yss", "scaffold-generation.json");
|
|
55
|
+
const contractPath = path.join(root, "slice-contract.json");
|
|
56
|
+
await mkdir(path.dirname(manifestPath), { recursive: true });
|
|
57
|
+
await writeFile(manifestPath, JSON.stringify({
|
|
58
|
+
schema_version: 2,
|
|
59
|
+
contract_id: "scaffold-1",
|
|
60
|
+
contract_version: 1,
|
|
61
|
+
scaffold_request_id: "scaffold-request-1",
|
|
62
|
+
contract_digest: "a".repeat(64),
|
|
63
|
+
profiles: { architecture: "target-domain-model", persistence: "mybatis-plus", database: "mysql", platform: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web", repository: "yss-internal" },
|
|
64
|
+
ownership: { generated_files: [] },
|
|
65
|
+
readiness: omitReadiness ? { downstream_skills: {}, contracts: {}, architecture_ruleset: "b".repeat(64) } : await readiness(),
|
|
66
|
+
generation_policy: { mode: "initialize-only", existing_target: "unsupported", old_project_migration: "unsupported", template_upgrade: "unsupported" },
|
|
67
|
+
completion_level: "empty-scaffold-verified",
|
|
68
|
+
approval_ref: "approval-1",
|
|
69
|
+
approver: "maintainer",
|
|
70
|
+
lifecycle_approval_ref: "approval-1",
|
|
71
|
+
router_draft_ref: "router-1",
|
|
72
|
+
persisted_ref: "persisted-1",
|
|
73
|
+
contract_file_ref: contractPath,
|
|
74
|
+
current_version: 1,
|
|
75
|
+
allowed_write_paths: ["."],
|
|
76
|
+
expected_evidence_files: [".yss/scaffold-generation.json"],
|
|
77
|
+
verification_commands: ["./mvnw validate", "./mvnw test", "./mvnw package"],
|
|
78
|
+
generation_mode: "controlled-generation",
|
|
79
|
+
maven_coordinates_source: "approved-contract"
|
|
80
|
+
}));
|
|
81
|
+
await writeFile(contractPath, JSON.stringify({
|
|
82
|
+
schema_version: 1,
|
|
83
|
+
contract_id: "slice-1",
|
|
84
|
+
contract_version: 1,
|
|
85
|
+
slice_id: "quality-rule-first-slice",
|
|
86
|
+
status: "approved",
|
|
87
|
+
readiness: { blockers: [], stale_inputs: [] },
|
|
88
|
+
common: { required_skills: requiredSkills },
|
|
89
|
+
backend: { status: "required", affected_layers: ["domain", "application", "infrastructure", "web"], required_skills: requiredSkills },
|
|
90
|
+
work_units: [{ id: "slice-backend", contract_id: "slice-1", contract_version: 1, work_unit: { primary_skill: "yss-domain" } }]
|
|
91
|
+
}));
|
|
92
|
+
await write(project, "mvnw", "#!/bin/sh\nexit 0\n");
|
|
93
|
+
await chmod(path.join(project, "mvnw"), 0o755);
|
|
94
|
+
await write(project, ".mvn/settings.xml", "<settings/>\n");
|
|
95
|
+
await write(project, ".mvn/maven.config", "-s .mvn/settings.xml -P yss-internal\n");
|
|
96
|
+
const files = [
|
|
97
|
+
"demo-service-domain/src/main/java/com/yss/demo/domain/quality/model/QualityRule.java",
|
|
98
|
+
"demo-service-domain/src/main/java/com/yss/demo/domain/quality/gateway/QualityRuleGateway.java",
|
|
99
|
+
"demo-service-domain/src/test/java/com/yss/demo/domain/quality/model/QualityRuleTest.java",
|
|
100
|
+
"demo-service-application/src/main/java/com/yss/demo/application/command/QualityRuleCreateCommand.java",
|
|
101
|
+
"demo-service-application/src/main/java/com/yss/demo/application/query/QualityRulePageQuery.java",
|
|
102
|
+
"demo-service-application/src/main/java/com/yss/demo/application/result/QualityRuleResult.java",
|
|
103
|
+
"demo-service-application/src/main/java/com/yss/demo/application/service/QualityRuleService.java",
|
|
104
|
+
"demo-service-application/src/main/java/com/yss/demo/application/service/impl/QualityRuleServiceImpl.java",
|
|
105
|
+
"demo-service-application/src/test/java/com/yss/demo/application/service/QualityRuleServiceTest.java",
|
|
106
|
+
"demo-service-infrastructure/src/main/java/com/yss/demo/infrastructure/persistence/po/QualityRulePO.java",
|
|
107
|
+
"demo-service-infrastructure/src/main/java/com/yss/demo/infrastructure/persistence/repository/QualityRuleRepository.java",
|
|
108
|
+
"demo-service-infrastructure/src/main/java/com/yss/demo/infrastructure/persistence/convertor/QualityRulePersistenceConvertor.java",
|
|
109
|
+
"demo-service-infrastructure/src/main/java/com/yss/demo/infrastructure/query/adapter/QualityRuleQueryAdapter.java",
|
|
110
|
+
"demo-service-infrastructure/src/test/java/com/yss/demo/infrastructure/persistence/QualityRuleGatewayIntegrationTest.java",
|
|
111
|
+
"demo-service-adapter/demo-service-web/src/main/java/com/yss/demo/rest/QualityRuleController.java",
|
|
112
|
+
"demo-service-adapter/demo-service-web/src/main/java/com/yss/demo/rest/convertor/QualityRuleWebConvertor.java",
|
|
113
|
+
"demo-service-adapter/demo-service-web/src/main/java/com/yss/demo/rest/dto/request/QualityRuleCreateRequest.java",
|
|
114
|
+
"demo-service-adapter/demo-service-web/src/main/java/com/yss/demo/rest/dto/response/QualityRuleResponse.java",
|
|
115
|
+
"demo-service-adapter/demo-service-web/src/test/java/com/yss/demo/rest/QualityRuleContractTest.java"
|
|
116
|
+
];
|
|
117
|
+
if (!omitGatewayImpl) files.push("demo-service-infrastructure/src/main/java/com/yss/demo/infrastructure/persistence/gateway/QualityRuleGatewayImpl.java");
|
|
118
|
+
for (const relative of files) await write(project, relative);
|
|
119
|
+
return { root, project, evidence, manifestPath, contractPath };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const environment = { ...process.env, JAVA_HOME: "/fixture/java", YSS_MAVEN_REPOSITORY_URL: "https://repo.example.invalid", MAVEN_REPO_USERNAME: "user", MAVEN_REPO_PASSWORD: "secret" };
|
|
123
|
+
|
|
124
|
+
test("refuses first-slice promotion when a required persistence seam is absent", async () => {
|
|
125
|
+
const data = await fixture({ omitGatewayImpl: true });
|
|
126
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
127
|
+
assert.equal(report.status, "failed");
|
|
128
|
+
assert.ok(report.missing_artifacts.includes("infrastructure-gateway-implementation"));
|
|
129
|
+
const manifest = JSON.parse(await readFile(data.manifestPath, "utf8"));
|
|
130
|
+
assert.equal(manifest.completion_level, "empty-scaffold-verified");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("refuses first-slice promotion when downstream skill or contract digests are absent", async () => {
|
|
134
|
+
const data = await fixture({ omitReadiness: true });
|
|
135
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
136
|
+
assert.equal(report.status, "failed");
|
|
137
|
+
assert.ok(report.downstream_skill_drift.includes("yss-domain:digest-missing"));
|
|
138
|
+
assert.ok(report.downstream_skill_drift.includes("router_contract:digest-missing"));
|
|
139
|
+
const manifest = JSON.parse(await readFile(data.manifestPath, "utf8"));
|
|
140
|
+
assert.equal(manifest.completion_level, "empty-scaffold-verified");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("refuses first-slice promotion when a work unit references a stale contract version", async () => {
|
|
144
|
+
const data = await fixture();
|
|
145
|
+
const contract = JSON.parse(await readFile(data.contractPath, "utf8"));
|
|
146
|
+
contract.work_units[0].contract_version = 0;
|
|
147
|
+
await writeFile(data.contractPath, JSON.stringify(contract));
|
|
148
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
149
|
+
assert.equal(report.status, "failed");
|
|
150
|
+
assert.ok(report.contract_failures.includes("work-unit-contract-version-mismatch:slice-backend"));
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("promotes a complete approved slice only after all root wrapper commands pass", async () => {
|
|
154
|
+
const data = await fixture();
|
|
155
|
+
const report = await runFirstSliceVerification(data.project, data.evidence, data.contractPath, environment);
|
|
156
|
+
assert.equal(report.status, "passed");
|
|
157
|
+
assert.equal(report.completion_level, "first-slice-verified");
|
|
158
|
+
assert.deepEqual(report.commands.map((item) => item.exit_code), [0, 0, 0]);
|
|
159
|
+
const manifest = JSON.parse(await readFile(data.manifestPath, "utf8"));
|
|
160
|
+
assert.equal(manifest.completion_level, "first-slice-verified");
|
|
161
|
+
assert.equal(manifest.first_slice_contract.contract_id, "slice-1");
|
|
162
|
+
assert.equal(manifest.first_slice_contract.slice_id, "quality-rule-first-slice");
|
|
163
|
+
assert.equal(JSON.parse(await readFile(path.join(data.evidence, "first-slice-verification.json"), "utf8")).status, "passed");
|
|
164
|
+
});
|
|
@@ -25,7 +25,19 @@ const SUPPORTED_PROFILES = Object.freeze({
|
|
|
25
25
|
dto_placement: "web",
|
|
26
26
|
repository: "yss-internal"
|
|
27
27
|
});
|
|
28
|
-
const DOWNSTREAM_SKILLS = [
|
|
28
|
+
const DOWNSTREAM_SKILLS = [
|
|
29
|
+
"yss-domain",
|
|
30
|
+
"yss-application",
|
|
31
|
+
"yss-repository",
|
|
32
|
+
"yss-mybatis",
|
|
33
|
+
"yss-web-controller",
|
|
34
|
+
"yss-dto",
|
|
35
|
+
"yss-exception",
|
|
36
|
+
"yss-validation",
|
|
37
|
+
"mapstruct",
|
|
38
|
+
"lombok",
|
|
39
|
+
"alibaba-java-code-style"
|
|
40
|
+
];
|
|
29
41
|
function fail(message) { throw new Error(message); }
|
|
30
42
|
function isoNow() { return new Date().toISOString(); }
|
|
31
43
|
function localDate() { return new Date().toISOString().slice(0, 10); }
|
|
@@ -324,10 +336,12 @@ class ScaffoldGenerator {
|
|
|
324
336
|
}
|
|
325
337
|
const downstream = {};
|
|
326
338
|
for (const skill of DOWNSTREAM_SKILLS) {
|
|
327
|
-
const
|
|
328
|
-
downstream[skill] = await isFile(
|
|
339
|
+
const skillRoot = path.join(REPOSITORY_ROOT, ".agents", "skills", skill);
|
|
340
|
+
downstream[skill] = await isFile(path.join(skillRoot, "SKILL.md")) ? await treeDigest(skillRoot) : "unavailable";
|
|
329
341
|
}
|
|
330
|
-
const
|
|
342
|
+
const scaffoldParent = path.join(SKILL_ROOT, "references", "yss-backend-scaffold-parent", "SKILL.md");
|
|
343
|
+
const routerContract = path.join(REPOSITORY_ROOT, ".agents", "skills", "yss-router", "references", "router-contract.yaml");
|
|
344
|
+
const manifest = { schema_version: 2, contract_id: this.options.contractId, contract_version: this.options.contractVersion, scaffold_request_id: contract.scaffold_request_id, approval_ref: this.options.approvalRef, router_draft_ref: this.options.routerDraftRef, persisted_ref: this.options.persistedRef, contract_file_ref: this.contractFile, contract_digest: this.contractDigest, lifecycle_approval_ref: contract.lifecycle_approval_ref, current_version: contract.current_version, approver: contract.approval.approver, allowed_write_paths: contract.allowed_write_paths, expected_evidence_files: contract.expected_evidence_files, project_name: this.projectName, base_package: this.basePackage, bootstrap_main_class: `${this.basePackage}.${this.applicationClassName}`, bootstrap_main_source: this.bootstrapMainSource, maven_coordinates: this.mavenCoordinates, maven_coordinates_source: this.mavenCoordinatesSource, profiles: this.profiles, database: this.options.database, generation_mode: "controlled-generation", completion_level: "generated", generator: { id: "yss-ddd-scaffold-generator", template_digest: await treeDigest(path.join(SKILL_ROOT, "assets")) }, ownership: { generated_files: generatedFiles, user_owned_globs: ["**/src/main/java/**", "**/src/test/java/**", "db/**"] }, readiness: { downstream_skills: downstream, contracts: { scaffold_parent: sha256(await readFile(scaffoldParent)), router_contract: sha256(await readFile(routerContract)) }, architecture_ruleset: sha256(await readFile(path.join(this.javaTemplateDir, "architecture-rules-test.java.template"))) }, generation_policy: { mode: "initialize-only", existing_target: "unsupported", old_project_migration: "unsupported", template_upgrade: "unsupported" }, verification_commands: COMMANDS, generated_at: isoNow() };
|
|
331
345
|
await writeText(path.join(this.projectRoot, ".yss", "scaffold-generation.json"), `${JSON.stringify(manifest, null, 2)}\n`); console.log(" ✓ .yss/scaffold-generation.json");
|
|
332
346
|
}
|
|
333
347
|
async copyWrapperFiles() {
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Promote a generated backend only after a complete, approved first vertical slice passes real Wrapper verification. */
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { mkdir, readFile, readdir, rename, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
import { run as runScaffoldVerification } from "./run_scaffold_verification.mjs";
|
|
10
|
+
|
|
11
|
+
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const REPOSITORY_ROOT = path.resolve(SCRIPT_DIR, "../../../..");
|
|
13
|
+
const REQUIRED_SKILLS = ["yss-domain", "yss-application", "yss-repository", "yss-mybatis", "yss-web-controller", "yss-dto", "yss-exception", "yss-validation", "mapstruct", "lombok", "alibaba-java-code-style"];
|
|
14
|
+
const REQUIRED_LAYERS = ["domain", "application", "infrastructure", "web"];
|
|
15
|
+
const ARTIFACT_CHECKS = [
|
|
16
|
+
["domain-model", /-domain\/src\/main\/java\/.+\/domain\/.+\/model\/.+\.java$/],
|
|
17
|
+
["domain-gateway", /-domain\/src\/main\/java\/.+\/domain\/.+\/gateway\/.+Gateway\.java$/],
|
|
18
|
+
["domain-behavior-test", /-domain\/src\/test\/java\/.+(?:Test|Tests)\.java$/],
|
|
19
|
+
["application-command-or-query", /-application\/src\/main\/java\/.+\/application\/(?:command|query)\/.+\.java$/],
|
|
20
|
+
["application-result", /-application\/src\/main\/java\/.+\/application\/result\/.+Result\.java$/],
|
|
21
|
+
["application-service", /-application\/src\/main\/java\/.+\/application\/service\/.+Service\.java$/],
|
|
22
|
+
["application-service-implementation", /-application\/src\/main\/java\/.+\/application\/service\/impl\/.+ServiceImpl\.java$/],
|
|
23
|
+
["application-behavior-test", /-application\/src\/test\/java\/.+(?:Test|Tests)\.java$/],
|
|
24
|
+
["infrastructure-po", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/po\/.+PO\.java$/],
|
|
25
|
+
["infrastructure-repository", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/repository\/.+Repository\.java$/],
|
|
26
|
+
["infrastructure-convertor", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/convertor\/.+Convertor\.java$/],
|
|
27
|
+
["infrastructure-gateway-implementation", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/persistence\/gateway\/.+GatewayImpl\.java$/],
|
|
28
|
+
["infrastructure-query-adapter", /-infrastructure\/src\/main\/java\/.+\/infrastructure\/query\/adapter\/.+QueryAdapter\.java$/],
|
|
29
|
+
["infrastructure-integration-test", /-infrastructure\/src\/test\/java\/.+(?:Test|Tests)\.java$/],
|
|
30
|
+
["web-controller", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/.+Controller\.java$/],
|
|
31
|
+
["web-convertor", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/convertor\/.+WebConvertor\.java$/],
|
|
32
|
+
["web-request", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/dto\/request\/.+Request\.java$/],
|
|
33
|
+
["web-response", /-adapter\/.+-web\/src\/main\/java\/.+\/rest\/dto\/response\/.+Response\.java$/],
|
|
34
|
+
["web-contract-test", /-adapter\/.+-web\/src\/test\/java\/.+(?:Test|Tests)\.java$/]
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const isoNow = () => new Date().toISOString();
|
|
38
|
+
const sha256 = (content) => createHash("sha256").update(content).digest("hex");
|
|
39
|
+
async function isFile(target) { try { return (await stat(target)).isFile(); } catch (error) { if (error.code === "ENOENT") return false; throw error; } }
|
|
40
|
+
async function files(root) {
|
|
41
|
+
const output = [];
|
|
42
|
+
async function visit(directory) {
|
|
43
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
44
|
+
const absolute = path.join(directory, entry.name);
|
|
45
|
+
if (entry.isDirectory()) await visit(absolute);
|
|
46
|
+
else if (entry.isFile()) output.push(path.relative(root, absolute).split(path.sep).join("/"));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
await visit(root);
|
|
50
|
+
return output.sort();
|
|
51
|
+
}
|
|
52
|
+
async function treeDigest(root) {
|
|
53
|
+
const hash = createHash("sha256");
|
|
54
|
+
for (const relative of await files(root)) hash.update(relative).update("\0").update(await readFile(path.join(root, relative))).update("\0");
|
|
55
|
+
return hash.digest("hex");
|
|
56
|
+
}
|
|
57
|
+
async function writeJsonAtomic(target, value) {
|
|
58
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
59
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
60
|
+
await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
61
|
+
await rename(temporary, target);
|
|
62
|
+
}
|
|
63
|
+
function invalidContractReasons(contract) {
|
|
64
|
+
const reasons = [];
|
|
65
|
+
if (contract.schema_version !== 1) reasons.push("slice-contract-schema");
|
|
66
|
+
if (contract.status !== "approved") reasons.push("slice-contract-not-approved");
|
|
67
|
+
for (const field of ["contract_id", "contract_version", "slice_id"]) if (contract[field] === undefined || contract[field] === null || contract[field] === "") reasons.push(`slice-contract-${field}-missing`);
|
|
68
|
+
if (contract.readiness?.blockers?.length) reasons.push("slice-contract-has-blockers");
|
|
69
|
+
if (contract.readiness?.stale_inputs?.length) reasons.push("slice-contract-has-stale-inputs");
|
|
70
|
+
const actualSkills = new Set([...(contract.common?.required_skills ?? []), ...(contract.backend?.required_skills ?? [])]);
|
|
71
|
+
for (const skill of REQUIRED_SKILLS) if (!actualSkills.has(skill)) reasons.push(`required-skill-missing:${skill}`);
|
|
72
|
+
const actualLayers = new Set(contract.backend?.affected_layers ?? []);
|
|
73
|
+
for (const layer of REQUIRED_LAYERS) if (!actualLayers.has(layer)) reasons.push(`required-layer-missing:${layer}`);
|
|
74
|
+
if (!Array.isArray(contract.work_units) || !contract.work_units.length) reasons.push("slice-contract-work-units-missing");
|
|
75
|
+
else for (const [index, unit] of contract.work_units.entries()) {
|
|
76
|
+
const id = unit.id || `index-${index}`;
|
|
77
|
+
if (unit.contract_id !== contract.contract_id) reasons.push(`work-unit-contract-id-mismatch:${id}`);
|
|
78
|
+
if (unit.contract_version !== contract.contract_version) reasons.push(`work-unit-contract-version-mismatch:${id}`);
|
|
79
|
+
if (!unit.work_unit?.primary_skill) reasons.push(`work-unit-primary-skill-missing:${id}`);
|
|
80
|
+
}
|
|
81
|
+
return reasons;
|
|
82
|
+
}
|
|
83
|
+
async function downstreamDrift(manifest) {
|
|
84
|
+
const drift = [];
|
|
85
|
+
const recordedSkills = manifest.readiness?.downstream_skills ?? {};
|
|
86
|
+
for (const skill of REQUIRED_SKILLS) {
|
|
87
|
+
const expected = recordedSkills[skill];
|
|
88
|
+
if (typeof expected !== "string" || !/^[a-f0-9]{64}$/.test(expected)) {
|
|
89
|
+
drift.push(`${skill}:digest-missing`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const root = path.join(REPOSITORY_ROOT, ".agents", "skills", skill);
|
|
93
|
+
if (!await isFile(path.join(root, "SKILL.md"))) drift.push(`${skill}:unavailable`);
|
|
94
|
+
else if (await treeDigest(root) !== expected) drift.push(`${skill}:digest-mismatch`);
|
|
95
|
+
}
|
|
96
|
+
const contracts = manifest.readiness?.contracts ?? {};
|
|
97
|
+
const contractFiles = {
|
|
98
|
+
scaffold_parent: path.join(REPOSITORY_ROOT, ".agents", "skills", "yss-ddd-scaffold-generator", "references", "yss-backend-scaffold-parent", "SKILL.md"),
|
|
99
|
+
router_contract: path.join(REPOSITORY_ROOT, ".agents", "skills", "yss-router", "references", "router-contract.yaml")
|
|
100
|
+
};
|
|
101
|
+
for (const [name, target] of Object.entries(contractFiles)) {
|
|
102
|
+
const expected = contracts[name];
|
|
103
|
+
if (typeof expected !== "string" || !/^[a-f0-9]{64}$/.test(expected)) {
|
|
104
|
+
drift.push(`${name}:digest-missing`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (!await isFile(target)) drift.push(`${name}:unavailable`);
|
|
108
|
+
else if (sha256(await readFile(target)) !== expected) drift.push(`${name}:digest-mismatch`);
|
|
109
|
+
}
|
|
110
|
+
return drift;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export async function runFirstSliceVerification(projectRoot, evidenceDir, sliceContractFile, environment = process.env) {
|
|
114
|
+
projectRoot = path.resolve(projectRoot);
|
|
115
|
+
evidenceDir = path.resolve(evidenceDir);
|
|
116
|
+
sliceContractFile = path.resolve(sliceContractFile);
|
|
117
|
+
const manifestPath = path.join(projectRoot, ".yss", "scaffold-generation.json");
|
|
118
|
+
const reportPath = path.join(evidenceDir, "first-slice-verification.json");
|
|
119
|
+
if (!await isFile(manifestPath)) throw new Error(`missing scaffold manifest: ${manifestPath}`);
|
|
120
|
+
if (!await isFile(sliceContractFile)) throw new Error(`missing Slice Implementation Contract: ${sliceContractFile}`);
|
|
121
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
122
|
+
const contract = JSON.parse(await readFile(sliceContractFile, "utf8"));
|
|
123
|
+
const contractFailures = invalidContractReasons(contract);
|
|
124
|
+
if (manifest.schema_version !== 2 || manifest.completion_level !== "empty-scaffold-verified") contractFailures.push("scaffold-not-empty-scaffold-verified");
|
|
125
|
+
const projectFiles = await files(projectRoot);
|
|
126
|
+
const missingArtifacts = ARTIFACT_CHECKS.filter(([, pattern]) => !projectFiles.some((file) => pattern.test(file))).map(([name]) => name);
|
|
127
|
+
const skillDrift = await downstreamDrift(manifest);
|
|
128
|
+
if (contractFailures.length || missingArtifacts.length || skillDrift.length) {
|
|
129
|
+
const report = { verification_mode: "first-slice", project_root: projectRoot, slice_contract_ref: sliceContractFile, scaffold_manifest_ref: manifestPath, generated_at: isoNow(), status: "failed", completion_level: manifest.completion_level, contract_failures: contractFailures, missing_artifacts: missingArtifacts, downstream_skill_drift: skillDrift, commands: [] };
|
|
130
|
+
await writeJsonAtomic(reportPath, report);
|
|
131
|
+
return report;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const mavenEvidence = path.join(evidenceDir, "maven");
|
|
135
|
+
const wrapper = await runScaffoldVerification(projectRoot, mavenEvidence, environment);
|
|
136
|
+
const passed = wrapper.status === "passed";
|
|
137
|
+
const report = { verification_mode: "first-slice", project_root: projectRoot, slice_contract_ref: sliceContractFile, scaffold_manifest_ref: manifestPath, generated_at: isoNow(), status: passed ? "passed" : "failed", failure_category: wrapper.failure_category, completion_level: passed ? "first-slice-verified" : manifest.completion_level, contract_failures: [], missing_artifacts: [], downstream_skill_drift: [], commands: wrapper.commands };
|
|
138
|
+
await writeJsonAtomic(reportPath, report);
|
|
139
|
+
if (passed) {
|
|
140
|
+
manifest.completion_level = "first-slice-verified";
|
|
141
|
+
manifest.first_slice_verification_ref = reportPath;
|
|
142
|
+
manifest.first_slice_verified_at = isoNow();
|
|
143
|
+
manifest.first_slice_contract = { contract_id: contract.contract_id, contract_version: contract.contract_version, slice_id: contract.slice_id, sha256: sha256(await readFile(sliceContractFile)) };
|
|
144
|
+
await writeJsonAtomic(manifestPath, manifest);
|
|
145
|
+
}
|
|
146
|
+
return report;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function parseArgs(argv) {
|
|
150
|
+
const result = {};
|
|
151
|
+
for (let index = 0; index < argv.length; index += 2) {
|
|
152
|
+
const flag = argv[index], value = argv[index + 1];
|
|
153
|
+
if (!["--project-root", "--evidence-dir", "--slice-contract-file"].includes(flag) || !value) throw new Error("usage: run_first_slice_verification.mjs --project-root DIR --evidence-dir DIR --slice-contract-file FILE");
|
|
154
|
+
result[flag.slice(2).replaceAll(/-([a-z])/g, (_, letter) => letter.toUpperCase())] = value;
|
|
155
|
+
}
|
|
156
|
+
if (!result.projectRoot || !result.evidenceDir || !result.sliceContractFile) throw new Error("project root, evidence dir, and slice contract file are required");
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function main() {
|
|
161
|
+
try {
|
|
162
|
+
const args = parseArgs(process.argv.slice(2));
|
|
163
|
+
const report = await runFirstSliceVerification(args.projectRoot, args.evidenceDir, args.sliceContractFile);
|
|
164
|
+
process.stdout.write(`${report.status}: ${path.join(path.resolve(args.evidenceDir), "first-slice-verification.json")}\n`);
|
|
165
|
+
return report.status === "passed" ? 0 : 1;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
process.stderr.write(`first-slice verification failed: ${error.message}\n`);
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (process.argv[1] && import.meta.url === new URL(`file://${process.argv[1]}`).href) process.exitCode = await main();
|
package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { execFile } from "node:child_process";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
3
4
|
import { chmod, mkdtemp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
4
5
|
import { existsSync } from "node:fs";
|
|
5
6
|
import os from "node:os";
|
|
@@ -15,6 +16,18 @@ const generator = path.join(scripts, "generate_scaffold.mjs");
|
|
|
15
16
|
const workflow = path.join(scripts, "generate_and_verify_scaffold.mjs");
|
|
16
17
|
const command = (args, options = {}) => new Promise((resolve) => execFile(process.execPath, [generator, ...args], { encoding: "utf8", ...options }, (error, stdout, stderr) => resolve({ code: error?.code ?? 0, stdout, stderr })));
|
|
17
18
|
const workflowCommand = (args, options = {}) => new Promise((resolve) => execFile(process.execPath, [workflow, ...args], { encoding: "utf8", ...options }, (error, stdout, stderr) => resolve({ code: error?.code ?? 0, stdout, stderr })));
|
|
19
|
+
async function treeDigest(root) {
|
|
20
|
+
const hash = createHash("sha256");
|
|
21
|
+
async function visit(directory) {
|
|
22
|
+
for (const entry of (await readdir(directory, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
23
|
+
const absolute = path.join(directory, entry.name);
|
|
24
|
+
if (entry.isDirectory()) await visit(absolute);
|
|
25
|
+
else if (entry.isFile()) hash.update(path.relative(root, absolute).split(path.sep).join("/")).update("\0").update(await readFile(absolute)).update("\0");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await visit(root);
|
|
29
|
+
return hash.digest("hex");
|
|
30
|
+
}
|
|
18
31
|
function contract(outputDir, overrides = {}) {
|
|
19
32
|
return {
|
|
20
33
|
schema_version: 2,
|
|
@@ -99,7 +112,7 @@ async function prepareVerifierProject(project, manifest = targetManifest()) {
|
|
|
99
112
|
await writeFile(path.join(project, ".yss", "scaffold-generation.json"), `${JSON.stringify(manifest)}\n`);
|
|
100
113
|
}
|
|
101
114
|
|
|
102
|
-
test("生成批准的服务级 target profile 骨架,并写入可追溯 Manifest v2", async (t) => { const data = await fixture(); t.after(() => rm(data.root, { recursive: true, force: true })); const result = await command(data.args); assert.equal(result.code, 0, result.stderr); const project = path.join(data.output, "demo-service"); const manifest = JSON.parse(await readFile(path.join(project, ".yss/scaffold-generation.json"), "utf8")); assert.equal(manifest.schema_version, 2); assert.equal(manifest.scaffold_request_id, "scaffold-request-1"); assert.equal(manifest.slice_id, undefined); assert.equal(manifest.completion_level, "generated"); assert.equal(manifest.profiles.architecture, "target-domain-model"); assert.deepEqual(manifest.generation_policy, { mode: "initialize-only", existing_target: "unsupported", old_project_migration: "unsupported", template_upgrade: "unsupported" }); assert.match(manifest.generator.template_digest, /^[a-f0-9]{64}$/); assert.match(manifest.contract_digest, /^[a-f0-9]{64}$/); assert.ok(manifest.ownership.generated_files.length > 8); assert.ok(manifest.ownership.generated_files.every((item) => item.owner === "generator" && /^[a-f0-9]{64}$/.test(item.sha256))); assert.equal(manifest.generation_mode, "controlled-generation"); assert.deepEqual(manifest.verification_commands, ["./mvnw validate", "./mvnw test", "./mvnw package"]); assert.equal(manifest.bootstrap_main_class, "com.yss.demo.DemoServiceApplication"); assert.equal(manifest.bootstrap_main_source, "demo-service-bootstrap/src/main/java/com/yss/demo/DemoServiceApplication.java"); assert.match(await readFile(path.join(project, manifest.bootstrap_main_source), "utf8"), /class DemoServiceApplication/); assert.match(await readFile(path.join(project, "pom.xml"), "utf8"), /demo-service/); });
|
|
115
|
+
test("生成批准的服务级 target profile 骨架,并写入可追溯 Manifest v2", async (t) => { const data = await fixture(); t.after(() => rm(data.root, { recursive: true, force: true })); const result = await command(data.args); assert.equal(result.code, 0, result.stderr); const project = path.join(data.output, "demo-service"); const manifest = JSON.parse(await readFile(path.join(project, ".yss/scaffold-generation.json"), "utf8")); assert.equal(manifest.schema_version, 2); assert.equal(manifest.scaffold_request_id, "scaffold-request-1"); assert.equal(manifest.slice_id, undefined); assert.equal(manifest.completion_level, "generated"); assert.equal(manifest.profiles.architecture, "target-domain-model"); assert.deepEqual(manifest.generation_policy, { mode: "initialize-only", existing_target: "unsupported", old_project_migration: "unsupported", template_upgrade: "unsupported" }); assert.match(manifest.generator.template_digest, /^[a-f0-9]{64}$/); assert.match(manifest.contract_digest, /^[a-f0-9]{64}$/); assert.ok(manifest.ownership.generated_files.length > 8); assert.ok(manifest.ownership.generated_files.every((item) => item.owner === "generator" && /^[a-f0-9]{64}$/.test(item.sha256))); assert.equal(manifest.generation_mode, "controlled-generation"); assert.deepEqual(manifest.verification_commands, ["./mvnw validate", "./mvnw test", "./mvnw package"]); assert.equal(manifest.bootstrap_main_class, "com.yss.demo.DemoServiceApplication"); assert.equal(manifest.bootstrap_main_source, "demo-service-bootstrap/src/main/java/com/yss/demo/DemoServiceApplication.java"); assert.match(await readFile(path.join(project, manifest.bootstrap_main_source), "utf8"), /class DemoServiceApplication/); assert.match(await readFile(path.join(project, "pom.xml"), "utf8"), /demo-service/); assert.equal(manifest.readiness.downstream_skills["yss-domain"], await treeDigest(path.resolve(scripts, "../../yss-domain"))); assert.match(manifest.readiness.contracts.scaffold_parent, /^[a-f0-9]{64}$/); assert.match(manifest.readiness.contracts.router_contract, /^[a-f0-9]{64}$/); });
|
|
103
116
|
|
|
104
117
|
test("schema v1 scaffold contract is unsupported and is never upgraded", async (t) => {
|
|
105
118
|
const data = await fixture({ schema_version: 1 });
|
|
@@ -47,7 +47,8 @@ description: 用于构建或重构 YSS 领域层代码。当用户要求设计
|
|
|
47
47
|
|
|
48
48
|
- 命名体现业务语义,不照抄表名缩写。
|
|
49
49
|
- 生成代码应可编译,且没有跨层依赖泄漏。
|
|
50
|
-
-
|
|
50
|
+
- Domain Gateway interface 由本 skill 唯一拥有;Infrastructure 只能实现,不得由 `yss-repository` 反向创建或改写其签名。
|
|
51
|
+
- 对不确定规则返回 `new_impacts` / `drift` 并暂停;不要把未批准假设或 TODO 写进实现冒充已确认事实。
|
|
51
52
|
|
|
52
53
|
## 协同顺序
|
|
53
54
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
version: 1
|
|
2
2
|
interface:
|
|
3
3
|
display_name: "YSS Domain"
|
|
4
|
-
short_description: "
|
|
5
|
-
default_prompt: "Use $yss-domain to
|
|
4
|
+
short_description: "按批准战术模型实现聚合、规则与 Domain Gateway 接口"
|
|
5
|
+
default_prompt: "Use $yss-domain to implement approved aggregates, entities, business rules, domain tests, and the sole Domain Gateway interfaces; reroute instead of inventing unresolved rules."
|
|
@@ -41,6 +41,8 @@ DTO/VO 位于 `{base_package}.rest.dto`。独立 client Maven module 对当前 T
|
|
|
41
41
|
|
|
42
42
|
Gateway 描述领域能力,只交换 Domain Model、领域值和领域标识。例如:
|
|
43
43
|
|
|
44
|
+
Gateway interface 的签名由批准战术模型决定,并由 `yss-domain` 唯一创建和维护;Infrastructure / `yss-repository` 只能实现该接口。若实现所需能力超出既有签名,返回 `new_impacts` 并重路由,不得从 DDL 反向扩写 Domain 合同。
|
|
45
|
+
|
|
44
46
|
```java
|
|
45
47
|
package com.yss.quality.domain.template.gateway;
|
|
46
48
|
|