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
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { constants, access, copyFile, mkdir, mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
3
5
|
import path from "node:path";
|
|
4
6
|
import { fileURLToPath } from "node:url";
|
|
5
7
|
|
|
8
|
+
import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
|
|
9
|
+
|
|
6
10
|
const TYPE_MAPPING = { bigint: "Long", int: "Integer", integer: "Integer", smallint: "Integer", tinyint: "Integer", number: "Long", numeric: "BigDecimal", decimal: "BigDecimal", float: "Float", double: "Double", real: "Float", varchar: "String", varchar2: "String", nvarchar2: "String", char: "String", nchar: "String", text: "String", clob: "String", nclob: "String", date: "LocalDateTime", datetime: "LocalDateTime", timestamp: "LocalDateTime", "timestamp with time zone": "LocalDateTime", boolean: "Boolean", bool: "Boolean", bit: "Boolean", json: "String", jsonb: "String", uuid: "String" };
|
|
7
11
|
const PLATFORM_VALIDATION_NAMESPACES = {
|
|
8
12
|
"spring-boot-2.7-jdk8": "javax",
|
|
9
13
|
"spring-boot-3-jdk17": "jakarta"
|
|
10
14
|
};
|
|
11
|
-
const required = ["metadata-file", "contract-file", "base-package", "module-name", "domain-segment"];
|
|
12
|
-
const valued = new Set([...required, "output-dir", "web-project-dir", "web-output-dir", "author", "application-service-package", "validation-namespace"]);
|
|
15
|
+
const required = ["metadata-file", "contract-file", "dto-wire-profile-file", "base-package", "module-name", "domain-segment"];
|
|
16
|
+
const valued = new Set([...required, "output-dir", "web-project-dir", "web-output-dir", "author", "application-service-package", "validation-namespace", "scaffold-manifest-file"]);
|
|
13
17
|
|
|
14
18
|
function usage() {
|
|
15
|
-
return "Usage: node generate_controller.mjs --metadata-file FILE --contract-file FILE --base-package PACKAGE --module-name NAME --domain-segment SEGMENT [--output-dir DIR] [--web-project-dir DIR] [--web-output-dir DIR] [--author NAME] [--application-service-package PACKAGE] [--validation-namespace javax|jakarta]";
|
|
19
|
+
return "Usage: node generate_controller.mjs --metadata-file FILE --contract-file FILE --dto-wire-profile-file FILE --base-package PACKAGE --module-name NAME --domain-segment SEGMENT [--output-dir DIR] [--web-project-dir DIR] [--web-output-dir DIR] [--scaffold-manifest-file FILE] [--author NAME] [--application-service-package PACKAGE] [--validation-namespace javax|jakarta]";
|
|
16
20
|
}
|
|
17
21
|
export function parseArgs(argv) {
|
|
18
22
|
const args = { "output-dir": "./output", author: "System", "validation-namespace": "javax", force: false };
|
|
@@ -51,6 +55,114 @@ function fields(columns, selected, { command = false } = {}) {
|
|
|
51
55
|
}
|
|
52
56
|
function render(template, context) { return template.replace(/\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g, (_, braced, plain) => { const key = braced || plain; if (!(key in context)) throw new Error(`missing template field: ${key}`); return context[key]; }); }
|
|
53
57
|
async function exists(target) { try { await access(target); return true; } catch { return false; } }
|
|
58
|
+
const present = (value) => value !== undefined && value !== null && value !== "";
|
|
59
|
+
function within(root, target) {
|
|
60
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
61
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
62
|
+
}
|
|
63
|
+
function simpleJavaName(javaType) {
|
|
64
|
+
return javaType.replace(/<.*$/, "").split(".").at(-1);
|
|
65
|
+
}
|
|
66
|
+
function paginationFields(selected, dtoProfile) {
|
|
67
|
+
const definitions = dtoProfile.page_query.fields;
|
|
68
|
+
const unique = [...new Set(selected)];
|
|
69
|
+
const unsupported = unique.filter((name) => !(name in definitions) || dtoProfile.page_query.forbidden_client_fields.includes(name));
|
|
70
|
+
if (unsupported.length) throw new Error(`unsupported pagination fields: ${unsupported.join(", ")}`);
|
|
71
|
+
return unique.map((name) => {
|
|
72
|
+
const definition = definitions[name];
|
|
73
|
+
const wireType = definition.wire_types.find((item) => item !== "null");
|
|
74
|
+
const javaType = wireType === "integer" ? (definition.nullable ? "Integer" : "int") : wireType === "string" ? "String" : null;
|
|
75
|
+
if (!javaType) throw new Error(`unsupported yss-dto pagination wire type for ${name}: ${definition.wire_types.join(", ")}`);
|
|
76
|
+
const annotation = definition.enum?.length
|
|
77
|
+
? ` @Pattern(regexp = "${definition.enum.join("|")}", message = "${name} must be one of ${definition.enum.join(", ")}")\n`
|
|
78
|
+
: "";
|
|
79
|
+
const initializer = definition.default === undefined ? "" : ` = ${typeof definition.default === "string" ? JSON.stringify(definition.default) : definition.default}`;
|
|
80
|
+
return `${annotation} private ${javaType} ${name}${initializer};`;
|
|
81
|
+
}).join("\n");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function loadDtoWireProfile(contract, args) {
|
|
85
|
+
const profilePath = path.resolve(args["dto-wire-profile-file"]);
|
|
86
|
+
if (profilePath !== path.resolve(contract.dto_wire_profile_ref ?? "")) throw new Error("--dto-wire-profile-file does not match the approved Web generation contract");
|
|
87
|
+
if (!await exists(profilePath)) throw new Error(`yss-dto wire profile not found: ${profilePath}`);
|
|
88
|
+
const source = await readFile(profilePath);
|
|
89
|
+
const digest = createHash("sha256").update(source).digest("hex");
|
|
90
|
+
if (digest !== contract.dto_wire_profile_digest) throw new Error("yss-dto wire profile digest does not match the approved Web generation contract");
|
|
91
|
+
const document = parseDocument(source.toString("utf8"), { maxAliasCount: 0, uniqueKeys: true });
|
|
92
|
+
if (document.errors.length) throw new Error(`invalid yss-dto wire profile: ${document.errors[0].message}`);
|
|
93
|
+
const profile = document.toJS({ maxAliasCount: 0 });
|
|
94
|
+
if (profile?.schema_version !== 1 || profile?.kind !== "yss-dto-openapi-wire-profile") throw new Error("unsupported yss-dto wire profile");
|
|
95
|
+
const resultPackage = profile.canonical?.java_package;
|
|
96
|
+
const pageResult = profile.wrappers?.PageResult;
|
|
97
|
+
const singleResult = profile.wrappers?.SingleResult;
|
|
98
|
+
const commandDtoType = profile.request_bases?.command?.java_type;
|
|
99
|
+
const queryDtoType = profile.request_bases?.query?.java_type;
|
|
100
|
+
if (![resultPackage, pageResult?.java_type, singleResult?.java_type, commandDtoType, queryDtoType].every(present)) throw new Error("incomplete yss-dto wire profile type mapping");
|
|
101
|
+
if (![pageResult.java_type, singleResult.java_type].every((type) => type.startsWith(`${resultPackage}.`))) throw new Error("yss-dto result wrapper package is inconsistent");
|
|
102
|
+
if (!profile.page_query?.fields || !Array.isArray(profile.page_query?.forbidden_client_fields)) throw new Error("incomplete yss-dto page_query mapping");
|
|
103
|
+
const factory = pageResult.java_factory;
|
|
104
|
+
const singleFactory = singleResult.java_factory;
|
|
105
|
+
const javaIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
106
|
+
if (!javaIdentifier.test(factory?.method ?? "") || !Array.isArray(factory.arguments) || !factory.arguments.length) throw new Error("incomplete yss-dto PageResult factory mapping");
|
|
107
|
+
if (!javaIdentifier.test(singleFactory?.method ?? "") || JSON.stringify(singleFactory.arguments) !== JSON.stringify(["data"])) throw new Error("incomplete yss-dto SingleResult factory mapping");
|
|
108
|
+
const expressionByArgument = { data: "toResponses(source.getData())", totalCount: "source.getTotalCount()", pageSize: "source.getPageSize()", pageIndex: "source.getPageIndex()" };
|
|
109
|
+
const unsupportedArguments = factory.arguments.filter((name) => !(name in expressionByArgument));
|
|
110
|
+
if (unsupportedArguments.length) throw new Error(`unsupported yss-dto PageResult factory arguments: ${unsupportedArguments.join(", ")}`);
|
|
111
|
+
return {
|
|
112
|
+
profile,
|
|
113
|
+
resultPackage,
|
|
114
|
+
pageResultType: pageResult.java_type.replace(/<.*$/, ""),
|
|
115
|
+
singleResultType: singleResult.java_type.replace(/<.*$/, ""),
|
|
116
|
+
pageResultFactoryMethod: factory.method,
|
|
117
|
+
singleResultFactoryMethod: singleFactory.method,
|
|
118
|
+
commandDtoType,
|
|
119
|
+
queryDtoType,
|
|
120
|
+
pageResultFactoryArguments: factory.arguments.map((name, index) => ` ${expressionByArgument[name]}${index < factory.arguments.length - 1 ? "," : ""}`).join("\n")
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function validateContract(contract, args) {
|
|
125
|
+
if (contract.schema_version !== 2) throw new Error(`web generation contract schema_version=${contract.schema_version} is unsupported; only schema_version=2 is accepted`);
|
|
126
|
+
if (contract.status !== "approved") throw new Error("web generation contract must be approved");
|
|
127
|
+
const requiredFields = ["contract_id", "contract_version", "current_version", "slice_id", "integration_mode", "implementation_project_root", "base_package", "module_name", "domain_segment", "application_service_package", "architecture_profile", "platform_profile", "validation_namespace", "dto_placement", "dto_wire_profile_ref", "dto_wire_profile_digest", "openapi_freeze_ref", "fields", "allowed_write_paths", "expected_evidence_files", "verification_commands"];
|
|
128
|
+
const missing = requiredFields.filter((name) => !present(contract[name]));
|
|
129
|
+
if (missing.length) throw new Error(`approved web generation contract is incomplete: ${missing.join(", ")}`);
|
|
130
|
+
if (contract.current_version !== contract.contract_version) throw new Error("web generation contract is not the current approved version");
|
|
131
|
+
if (!Number.isInteger(contract.contract_version) || contract.contract_version < 1) throw new Error("web generation contract_version must be a positive integer");
|
|
132
|
+
if (!["existing-project", "scaffold-v2"].includes(contract.integration_mode)) throw new Error("unsupported web generation integration_mode");
|
|
133
|
+
if (!["allowed_write_paths", "expected_evidence_files", "verification_commands"].every((name) => Array.isArray(contract[name]) && contract[name].length)) throw new Error("web generation contract requires non-empty allowed_write_paths, expected_evidence_files, and verification_commands");
|
|
134
|
+
if (contract.architecture_profile !== "target-domain-model" || contract.dto_placement !== "web") throw new Error("web generation only supports target-domain-model with dto_placement=web");
|
|
135
|
+
if (contract.base_package !== args["base-package"] || contract.module_name !== args["module-name"] || contract.domain_segment !== args["domain-segment"]) throw new Error("CLI generation identity does not match the approved web generation contract");
|
|
136
|
+
const applicationPackage = args["application-service-package"] || `${args["base-package"]}.application.service`;
|
|
137
|
+
if (contract.application_service_package !== applicationPackage) throw new Error("--application-service-package does not match the approved web generation contract");
|
|
138
|
+
if (!(contract.platform_profile in PLATFORM_VALIDATION_NAMESPACES)) throw new Error("unsupported platform_profile in approved web generation contract");
|
|
139
|
+
if (PLATFORM_VALIDATION_NAMESPACES[contract.platform_profile] !== contract.validation_namespace) throw new Error("platform_profile and validation_namespace are inconsistent");
|
|
140
|
+
if (contract.validation_namespace !== args["validation-namespace"]) throw new Error("--validation-namespace must match the approved web generation contract");
|
|
141
|
+
|
|
142
|
+
const implementationRoot = path.resolve(contract.implementation_project_root);
|
|
143
|
+
if (contract.integration_mode === "existing-project") {
|
|
144
|
+
if (args["scaffold-manifest-file"] || present(contract.scaffold_manifest_ref)) throw new Error("existing-project Web generation must not claim a scaffold manifest");
|
|
145
|
+
return { applicationPackage, implementationRoot };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!args["scaffold-manifest-file"] || !present(contract.scaffold_manifest_ref)) throw new Error("scaffold-v2 Web generation requires --scaffold-manifest-file and scaffold_manifest_ref");
|
|
149
|
+
const manifestPath = path.resolve(args["scaffold-manifest-file"]);
|
|
150
|
+
if (manifestPath !== path.resolve(contract.scaffold_manifest_ref)) throw new Error("--scaffold-manifest-file does not match the approved web generation contract");
|
|
151
|
+
if (!await exists(manifestPath)) throw new Error(`scaffold manifest not found: ${manifestPath}`);
|
|
152
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
153
|
+
if (manifest.schema_version !== 2) throw new Error("scaffold manifest must use schema_version=2");
|
|
154
|
+
if (!["empty-scaffold-verified", "first-slice-verified"].includes(manifest.completion_level)) throw new Error("scaffold manifest must be empty-scaffold-verified before Web generation");
|
|
155
|
+
const scaffoldRoot = path.dirname(path.dirname(manifestPath));
|
|
156
|
+
if (implementationRoot !== scaffoldRoot) throw new Error("implementation_project_root does not match the scaffold manifest project root");
|
|
157
|
+
const expectedWebProject = path.join(scaffoldRoot, `${manifest.project_name}-adapter`, `${manifest.project_name}-web`);
|
|
158
|
+
if (!args["web-project-dir"] || path.resolve(args["web-project-dir"]) !== expectedWebProject) throw new Error("--web-project-dir does not match the scaffold manifest project layout");
|
|
159
|
+
const profilePairs = [["architecture", "architecture_profile"], ["platform", "platform_profile"], ["validation_namespace", "validation_namespace"], ["dto_placement", "dto_placement"]];
|
|
160
|
+
for (const [manifestField, contractField] of profilePairs) {
|
|
161
|
+
if (manifest.profiles?.[manifestField] !== contract[contractField]) throw new Error(`scaffold manifest ${manifestField} profile does not match the approved Web contract`);
|
|
162
|
+
}
|
|
163
|
+
if (manifest.base_package !== contract.base_package) throw new Error("scaffold manifest base_package does not match the approved Web contract");
|
|
164
|
+
return { applicationPackage, implementationRoot };
|
|
165
|
+
}
|
|
54
166
|
|
|
55
167
|
export async function generate(args, logger = console) {
|
|
56
168
|
if (!await exists(args["metadata-file"])) throw new Error(`Metadata file not found: ${args["metadata-file"]}`);
|
|
@@ -59,14 +171,8 @@ export async function generate(args, logger = console) {
|
|
|
59
171
|
try { metadata = JSON.parse(await readFile(args["metadata-file"], "utf8")); } catch (error) { throw new Error(`Error reading metadata file: ${error.message}`); }
|
|
60
172
|
let contract;
|
|
61
173
|
try { contract = JSON.parse(await readFile(args["contract-file"], "utf8")); } catch (error) { throw new Error(`Error reading contract file: ${error.message}`); }
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (!contract.openapi_freeze_ref || !contract.fields || typeof contract.fields !== "object") throw new Error("approved web generation contract requires openapi_freeze_ref and fields");
|
|
65
|
-
if (!contract.base_package || !contract.module_name || !contract.domain_segment) throw new Error("approved web generation contract requires base_package, module_name, and domain_segment");
|
|
66
|
-
if (contract.base_package !== args["base-package"] || contract.module_name !== args["module-name"] || contract.domain_segment !== args["domain-segment"]) throw new Error("CLI generation identity does not match the approved web generation contract");
|
|
67
|
-
if (!(contract.platform_profile in PLATFORM_VALIDATION_NAMESPACES)) throw new Error("unsupported platform_profile in approved web generation contract");
|
|
68
|
-
if (PLATFORM_VALIDATION_NAMESPACES[contract.platform_profile] !== contract.validation_namespace) throw new Error("platform_profile and validation_namespace are inconsistent");
|
|
69
|
-
if (contract.validation_namespace !== args["validation-namespace"]) throw new Error("--validation-namespace must match the approved web generation contract");
|
|
174
|
+
const { applicationPackage, implementationRoot } = await validateContract(contract, args);
|
|
175
|
+
const dto = await loadDtoWireProfile(contract, args);
|
|
70
176
|
const tables = metadata.tables || [];
|
|
71
177
|
if (!tables.length) { logger.warn("Warning: No tables found in metadata."); return []; }
|
|
72
178
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -82,22 +188,37 @@ export async function generate(args, logger = console) {
|
|
|
82
188
|
for (const table of tables) {
|
|
83
189
|
const domainClass = pascal(table.table_name); const domainVar = lowerCamel(table.table_name); const domainDesc = String(table.table_comment || domainClass).trim().replaceAll("\n", " ");
|
|
84
190
|
const approvedFields = contract.fields[table.table_name];
|
|
85
|
-
if (!approvedFields || ["create", "update", "query", "response"].some((name) => !Array.isArray(approvedFields[name]))) throw new Error(`approved web field contract is missing for table: ${table.table_name}`);
|
|
86
|
-
const
|
|
87
|
-
const context = { base_package: args["base-package"], module_name: args["module-name"], domain_class: domainClass, domain_var: domainVar, domain_desc: domainDesc, domain_url_path: kebab(table.table_name), domain_pkg_name: args["domain-segment"], author: args.author, dto_imports: [`import ${args["base-package"]}.rest.dto.request.${domainClass}CreateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}UpdateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}PageRequest;`, `import ${args["base-package"]}.rest.dto.response.${domainClass}Response;`].join("\n"), application_type_imports: [`import ${args["base-package"]}.application.command.${domainClass}CreateCommand;`, `import ${args["base-package"]}.application.command.${domainClass}UpdateCommand;`, `import ${args["base-package"]}.application.query.${domainClass}PageQuery;`, `import ${args["base-package"]}.application.result.${domainClass}Result;`].join("\n"), application_service_import: `import ${applicationPackage}.${domainClass}Service;`, web_convertor_import: `import ${args["base-package"]}.rest.convertor.${domainClass}WebConvertor;`, web_convertor_class: `${domainClass}WebConvertor`, application_service_field: `private final ${domainClass}Service ${domainVar}Service;`, query_call: `${domainVar}Service.page(webConvertor.toPageQuery(request))`, detail_call: `${domainVar}Service.detail(id)`, add_call: `${domainVar}Service.add(webConvertor.toCreateCommand(request))`, update_call: `${domainVar}Service.update(webConvertor.toUpdateCommand(request))`, delete_call: `${domainVar}Service.delete(id)`, response_class: `${domainClass}Response`, create_request_class: `${domainClass}CreateRequest`, update_request_class: `${domainClass}UpdateRequest`, page_request_class: `${domainClass}PageRequest`, validation_namespace: args["validation-namespace"] };
|
|
191
|
+
if (!approvedFields || ["create", "update", "query", "pagination", "response"].some((name) => !Array.isArray(approvedFields[name]))) throw new Error(`approved web field contract is missing create/update/query/pagination/response allowlists for table: ${table.table_name}`);
|
|
192
|
+
const context = { base_package: args["base-package"], module_name: args["module-name"], domain_class: domainClass, domain_var: domainVar, domain_desc: domainDesc, domain_url_path: kebab(table.table_name), domain_pkg_name: args["domain-segment"], author: args.author, dto_imports: [`import ${args["base-package"]}.rest.dto.request.${domainClass}CreateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}UpdateRequest;`, `import ${args["base-package"]}.rest.dto.request.${domainClass}PageRequest;`, `import ${args["base-package"]}.rest.dto.response.${domainClass}Response;`].join("\n"), application_type_imports: [`import ${args["base-package"]}.application.command.${domainClass}CreateCommand;`, `import ${args["base-package"]}.application.command.${domainClass}UpdateCommand;`, `import ${args["base-package"]}.application.query.${domainClass}PageQuery;`, `import ${args["base-package"]}.application.result.${domainClass}Result;`].join("\n"), application_service_import: `import ${applicationPackage}.${domainClass}Service;`, web_convertor_import: `import ${args["base-package"]}.rest.convertor.${domainClass}WebConvertor;`, web_convertor_class: `${domainClass}WebConvertor`, application_service_field: `private final ${domainClass}Service ${domainVar}Service;`, query_call: `${domainVar}Service.page(webConvertor.toPageQuery(request))`, detail_call: `${domainVar}Service.detail(id)`, add_call: `${domainVar}Service.add(webConvertor.toCreateCommand(request))`, update_call: `${domainVar}Service.update(webConvertor.toUpdateCommand(request))`, delete_call: `${domainVar}Service.delete(id)`, response_class: `${domainClass}Response`, create_request_class: `${domainClass}CreateRequest`, update_request_class: `${domainClass}UpdateRequest`, page_request_class: `${domainClass}PageRequest`, validation_namespace: args["validation-namespace"], result_package: dto.resultPackage, page_result_type: dto.pageResultType, page_result_simple_name: simpleJavaName(dto.pageResultType), page_result_factory_method: dto.pageResultFactoryMethod, single_result_type: dto.singleResultType, single_result_simple_name: simpleJavaName(dto.singleResultType), single_result_factory_method: dto.singleResultFactoryMethod, command_dto_type: dto.commandDtoType, command_dto_simple_name: simpleJavaName(dto.commandDtoType), query_dto_type: dto.queryDtoType, query_dto_simple_name: simpleJavaName(dto.queryDtoType), page_result_factory_arguments: dto.pageResultFactoryArguments };
|
|
88
193
|
plan(path.join(webRoot, `${domainClass}Controller.java`), render(templates["Controller.java.template"], context));
|
|
89
194
|
plan(path.join(webRoot, "convertor", `${domainClass}WebConvertor.java`), render(templates["WebConvertor.java.template"], context));
|
|
90
|
-
for (const [templateName, target, declaration] of [["web/dto/Response.java.template", path.join(webRoot, "dto", "response", `${domainClass}Response.java`), fields(table.columns || [], approvedFields.response)], ["web/dto/CreateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}CreateRequest.java`), fields(table.columns || [], approvedFields.create, { command: true })], ["web/dto/UpdateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}UpdateRequest.java`), fields(table.columns || [], approvedFields.update, { command: true })], ["web/dto/PageRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}PageRequest.java`), fields(table.columns || [], approvedFields.query)]]) plan(target, render(templates[templateName], { ...context, field_declarations: declaration }));
|
|
195
|
+
for (const [templateName, target, declaration] of [["web/dto/Response.java.template", path.join(webRoot, "dto", "response", `${domainClass}Response.java`), fields(table.columns || [], approvedFields.response)], ["web/dto/CreateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}CreateRequest.java`), fields(table.columns || [], approvedFields.create, { command: true })], ["web/dto/UpdateRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}UpdateRequest.java`), fields(table.columns || [], approvedFields.update, { command: true })], ["web/dto/PageRequest.java.template", path.join(webRoot, "dto", "request", `${domainClass}PageRequest.java`), fields(table.columns || [], approvedFields.query)]]) plan(target, render(templates[templateName], { ...context, field_declarations: declaration, pagination_field_declarations: paginationFields(approvedFields.pagination, dto.profile) }));
|
|
91
196
|
}
|
|
92
197
|
const targets = planned.map(({ target }) => target);
|
|
93
198
|
if (new Set(targets).size !== targets.length) throw new Error("approved metadata produces duplicate target files");
|
|
94
199
|
const existing = [];
|
|
95
200
|
for (const target of targets) if (await exists(target)) existing.push(target);
|
|
96
201
|
if (existing.length) throw new Error(`initialize-only Web generation refuses existing targets: ${existing.join(", ")}`);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
202
|
+
const allowedRoots = contract.allowed_write_paths.map((item) => path.resolve(implementationRoot, String(item)));
|
|
203
|
+
const outside = targets.filter((target) => !allowedRoots.some((root) => within(root, target)));
|
|
204
|
+
if (outside.length) throw new Error(`planned Web targets are outside allowed_write_paths: ${outside.join(", ")}`);
|
|
205
|
+
const staging = await mkdtemp(path.join(tmpdir(), "yss-web-generation-"));
|
|
206
|
+
const created = [];
|
|
207
|
+
try {
|
|
208
|
+
for (const [index, { contents }] of planned.entries()) await writeFile(path.join(staging, String(index)), contents, "utf8");
|
|
209
|
+
for (const [index, { target }] of planned.entries()) {
|
|
210
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
211
|
+
await copyFile(path.join(staging, String(index)), target, constants.COPYFILE_EXCL);
|
|
212
|
+
created.push(target);
|
|
213
|
+
logger.log(`Generated: ${target}`);
|
|
214
|
+
}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
for (const target of created.reverse()) {
|
|
217
|
+
try { await unlink(target); } catch (cleanupError) { if (cleanupError.code !== "ENOENT") error.cleanupError = cleanupError.message; }
|
|
218
|
+
}
|
|
219
|
+
throw error;
|
|
220
|
+
} finally {
|
|
221
|
+
await rm(staging, { recursive: true, force: true });
|
|
101
222
|
}
|
|
102
223
|
return targets;
|
|
103
224
|
}
|
|
@@ -1,60 +1,156 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import test from "node:test";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
6
8
|
import { generate, parseArgs } from "./generate_controller.mjs";
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
const scripts = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const dtoWireProfileFile = path.resolve(scripts, "../../yss-dto/references/openapi-wire-profile.yaml");
|
|
12
|
+
const dtoWireProfileDigest = createHash("sha256").update(await readFile(dtoWireProfileFile)).digest("hex");
|
|
13
|
+
|
|
14
|
+
const metadataValue = {
|
|
15
|
+
tables: [{
|
|
16
|
+
table_name: "quality_rule",
|
|
17
|
+
table_comment: "质量规则",
|
|
18
|
+
columns: [
|
|
19
|
+
{ name: "id", sql_type: "bigint", primary: true, nullable: false },
|
|
20
|
+
{ name: "rule_name", sql_type: "varchar(64)", nullable: false }
|
|
21
|
+
]
|
|
22
|
+
}]
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function webContract(root, overrides = {}) {
|
|
26
|
+
return {
|
|
27
|
+
schema_version: 2,
|
|
28
|
+
contract_id: "slice-quality-web-1",
|
|
29
|
+
contract_version: 1,
|
|
30
|
+
current_version: 1,
|
|
31
|
+
slice_id: "quality-rule-create",
|
|
32
|
+
status: "approved",
|
|
33
|
+
integration_mode: "existing-project",
|
|
34
|
+
implementation_project_root: root,
|
|
35
|
+
base_package: "com.yss.demo",
|
|
36
|
+
module_name: "demo",
|
|
37
|
+
domain_segment: "quality",
|
|
38
|
+
application_service_package: "com.yss.demo.application.service",
|
|
39
|
+
architecture_profile: "target-domain-model",
|
|
40
|
+
platform_profile: "spring-boot-3-jdk17",
|
|
41
|
+
validation_namespace: "jakarta",
|
|
42
|
+
dto_placement: "web",
|
|
43
|
+
dto_wire_profile_ref: dtoWireProfileFile,
|
|
44
|
+
dto_wire_profile_digest: dtoWireProfileDigest,
|
|
45
|
+
openapi_freeze_ref: "openapi://quality-rule@1",
|
|
46
|
+
allowed_write_paths: [root],
|
|
47
|
+
expected_evidence_files: ["evidence/web-generation.json"],
|
|
48
|
+
verification_commands: ["./mvnw test", "./mvnw package"],
|
|
49
|
+
fields: {
|
|
50
|
+
quality_rule: {
|
|
51
|
+
create: ["rule_name"],
|
|
52
|
+
update: ["id", "rule_name"],
|
|
53
|
+
query: ["rule_name"],
|
|
54
|
+
pagination: ["pageIndex", "pageSize"],
|
|
55
|
+
response: ["id", "rule_name"]
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
...overrides
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function fixture(overrides = {}) {
|
|
9
63
|
const root = await mkdtemp(path.join(tmpdir(), "yss-controller-"));
|
|
10
64
|
const metadata = path.join(root, "metadata.json");
|
|
11
65
|
const contract = path.join(root, "web-contract.json");
|
|
12
|
-
|
|
13
|
-
await writeFile(
|
|
14
|
-
await
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
66
|
+
const output = path.join(root, "out");
|
|
67
|
+
await writeFile(metadata, JSON.stringify(metadataValue));
|
|
68
|
+
await writeFile(contract, JSON.stringify(webContract(root, overrides)));
|
|
69
|
+
const args = ["--metadata-file", metadata, "--contract-file", contract, "--dto-wire-profile-file", dtoWireProfileFile, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--output-dir", output, "--application-service-package", "com.yss.demo.application.service", "--validation-namespace", overrides.validation_namespace ?? "jakarta"];
|
|
70
|
+
return { root, metadata, contract, output, args };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
test("generates approved standalone Web code and exposes only approved pagination fields", async () => {
|
|
74
|
+
const data = await fixture();
|
|
75
|
+
await generate(parseArgs(data.args), { log() {}, warn() {} });
|
|
76
|
+
const rest = path.join(data.output, "com", "yss", "demo", "rest");
|
|
77
|
+
const controller = await readFile(path.join(rest, "QualityRuleController.java"), "utf8");
|
|
78
|
+
const convertor = await readFile(path.join(rest, "convertor", "QualityRuleWebConvertor.java"), "utf8");
|
|
79
|
+
const createRequest = await readFile(path.join(rest, "dto", "request", "QualityRuleCreateRequest.java"), "utf8");
|
|
80
|
+
const pageRequest = await readFile(path.join(rest, "dto", "request", "QualityRulePageRequest.java"), "utf8");
|
|
18
81
|
assert.match(controller, /import com\.yss\.demo\.application\.service\.QualityRuleService;/);
|
|
82
|
+
assert.match(controller, /SingleResult\.of\(/);
|
|
19
83
|
assert.match(controller, /import jakarta\.validation\.Valid;/);
|
|
20
|
-
assert.match(controller, /webConvertor\.toCreateCommand\(request\)/);
|
|
21
84
|
assert.match(convertor, /@Mapper\(componentModel = "spring"\)/);
|
|
22
|
-
assert.match(convertor, /
|
|
23
|
-
assert.match(convertor, /application\.result\.QualityRuleResult/);
|
|
85
|
+
assert.match(convertor, /PageResult\.of\(\s*toResponses\(source\.getData\(\)\),\s*source\.getTotalCount\(\),\s*source\.getPageSize\(\),\s*source\.getPageIndex\(\)/s);
|
|
24
86
|
assert.doesNotMatch(`${controller}\n${convertor}`, /Gateway|\.domain\.|Mappers\.getMapper|INSTANCE/);
|
|
25
87
|
assert.match(createRequest, /private String ruleName;/);
|
|
26
88
|
assert.doesNotMatch(createRequest, /private Long id;|@Data/);
|
|
89
|
+
assert.match(pageRequest, /private int pageIndex = 1;/);
|
|
90
|
+
assert.match(pageRequest, /private int pageSize = 10;/);
|
|
91
|
+
assert.doesNotMatch(pageRequest, /orderBy|orderDirection|groupBy|offset|needTotalCount|tempTotalCount|extends PageQuery/);
|
|
27
92
|
});
|
|
28
93
|
|
|
29
|
-
test("rejects
|
|
94
|
+
test("rejects a yss-dto wire profile whose digest differs from the approved contract", async () => {
|
|
95
|
+
const data = await fixture({ dto_wire_profile_digest: "0".repeat(64) });
|
|
96
|
+
await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }), /yss-dto wire profile digest/);
|
|
97
|
+
});
|
|
30
98
|
|
|
31
|
-
test("
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
await
|
|
37
|
-
const
|
|
38
|
-
await assert.rejects(() => generate(parseArgs(
|
|
39
|
-
|
|
40
|
-
await writeFile(contract, JSON.stringify({ schema_version: 1, status: "approved", base_package: "com.yss.demo", module_name: "demo", domain_segment: "quality", architecture_profile: "target-domain-model", platform_profile: "spring-boot-3-jdk17", validation_namespace: "javax", dto_placement: "web", openapi_freeze_ref: "openapi://quality-rule@1", fields: { quality_rule: { create: [], update: ["id"], query: [], response: ["id"] } } }));
|
|
41
|
-
await assert.rejects(() => generate(parseArgs(base), { log() {}, warn() {} }), /platform_profile and validation_namespace/);
|
|
42
|
-
assert.throws(() => parseArgs([...base, "--force"]), /initialize-only/);
|
|
99
|
+
test("rejects missing CLI arguments, schema v1, draft contracts, and inconsistent platform profiles", async () => {
|
|
100
|
+
assert.throws(() => parseArgs(["--module-name", "demo"]), /required/);
|
|
101
|
+
const v1 = await fixture({ schema_version: 1 });
|
|
102
|
+
await assert.rejects(() => generate(parseArgs(v1.args), { log() {}, warn() {} }), /schema_version=1.*unsupported/);
|
|
103
|
+
const draft = await fixture({ status: "draft" });
|
|
104
|
+
await assert.rejects(() => generate(parseArgs(draft.args), { log() {}, warn() {} }), /approved/);
|
|
105
|
+
const mismatch = await fixture({ platform_profile: "spring-boot-3-jdk17", validation_namespace: "javax" });
|
|
106
|
+
await assert.rejects(() => generate(parseArgs(mismatch.args), { log() {}, warn() {} }), /platform_profile and validation_namespace/);
|
|
107
|
+
assert.throws(() => parseArgs([...draft.args, "--force"]), /initialize-only/);
|
|
43
108
|
});
|
|
44
109
|
|
|
45
|
-
test("binds
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await writeFile(
|
|
51
|
-
|
|
52
|
-
const args = ["--metadata-file", metadata, "--contract-file", contract, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--output-dir", output];
|
|
53
|
-
await assert.rejects(() => generate(parseArgs(args.map((value) => value === "demo" ? "other" : value)), { log() {}, warn() {} }), /approved web generation contract/);
|
|
54
|
-
const existing = path.join(output, "com/yss/demo/rest/QualityRuleController.java");
|
|
110
|
+
test("binds identity and every target to the approved write scope before writing", async () => {
|
|
111
|
+
const data = await fixture();
|
|
112
|
+
await assert.rejects(() => generate(parseArgs(data.args.map((value) => value === "demo" ? "other" : value)), { log() {}, warn() {} }), /approved web generation contract/);
|
|
113
|
+
await writeFile(data.contract, JSON.stringify(webContract(data.root, { allowed_write_paths: [path.join(data.root, "different-root")] })));
|
|
114
|
+
await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }), /allowed_write_paths/);
|
|
115
|
+
await writeFile(data.contract, JSON.stringify(webContract(data.root)));
|
|
116
|
+
const existing = path.join(data.output, "com/yss/demo/rest/QualityRuleController.java");
|
|
55
117
|
await mkdir(path.dirname(existing), { recursive: true });
|
|
56
118
|
await writeFile(existing, "user-owned\n");
|
|
57
|
-
await assert.rejects(() => generate(parseArgs(args), { log() {}, warn() {} }), /initialize-only/);
|
|
119
|
+
await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }), /initialize-only/);
|
|
58
120
|
assert.equal(await readFile(existing, "utf8"), "user-owned\n");
|
|
59
|
-
await assert.rejects(() => readFile(path.join(output, "com/yss/demo/rest/convertor/QualityRuleWebConvertor.java"), "utf8"), /ENOENT/);
|
|
121
|
+
await assert.rejects(() => readFile(path.join(data.output, "com/yss/demo/rest/convertor/QualityRuleWebConvertor.java"), "utf8"), /ENOENT/);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("scaffold-v2 mode requires an empty-scaffold-verified manifest with matching profile and project layout", async () => {
|
|
125
|
+
const data = await fixture({ platform_profile: "spring-boot-2.7-jdk8", validation_namespace: "javax" });
|
|
126
|
+
const projectRoot = path.join(data.root, "demo-service");
|
|
127
|
+
const webProject = path.join(projectRoot, "demo-service-adapter", "demo-service-web");
|
|
128
|
+
const manifest = path.join(projectRoot, ".yss", "scaffold-generation.json");
|
|
129
|
+
await mkdir(path.dirname(manifest), { recursive: true });
|
|
130
|
+
await writeFile(manifest, JSON.stringify({ schema_version: 2, project_name: "demo-service", base_package: "com.yss.demo", completion_level: "empty-scaffold-verified", profiles: { architecture: "target-domain-model", platform: "spring-boot-2.7-jdk8", validation_namespace: "javax", dto_placement: "web" } }));
|
|
131
|
+
const contractValue = webContract(projectRoot, {
|
|
132
|
+
integration_mode: "scaffold-v2",
|
|
133
|
+
implementation_project_root: projectRoot,
|
|
134
|
+
scaffold_manifest_ref: manifest,
|
|
135
|
+
platform_profile: "spring-boot-3-jdk17",
|
|
136
|
+
validation_namespace: "jakarta",
|
|
137
|
+
allowed_write_paths: [path.join(webProject, "src/main/java/com/yss/demo/rest")]
|
|
138
|
+
});
|
|
139
|
+
await writeFile(data.contract, JSON.stringify(contractValue));
|
|
140
|
+
const args = ["--metadata-file", data.metadata, "--contract-file", data.contract, "--dto-wire-profile-file", dtoWireProfileFile, "--scaffold-manifest-file", manifest, "--base-package", "com.yss.demo", "--module-name", "demo", "--domain-segment", "quality", "--web-project-dir", webProject, "--application-service-package", "com.yss.demo.application.service", "--validation-namespace", "jakarta"];
|
|
141
|
+
await assert.rejects(() => generate(parseArgs(args), { log() {}, warn() {} }), /scaffold manifest.*platform/i);
|
|
142
|
+
contractValue.platform_profile = "spring-boot-2.7-jdk8";
|
|
143
|
+
contractValue.validation_namespace = "javax";
|
|
144
|
+
await writeFile(data.contract, JSON.stringify(contractValue));
|
|
145
|
+
await generate(parseArgs(args.map((value) => value === "jakarta" ? "javax" : value)), { log() {}, warn() {} });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("rolls back files written before a filesystem failure", async () => {
|
|
149
|
+
const data = await fixture();
|
|
150
|
+
const rest = path.join(data.output, "com/yss/demo/rest");
|
|
151
|
+
await mkdir(rest, { recursive: true });
|
|
152
|
+
await writeFile(path.join(rest, "convertor"), "blocking-file\n");
|
|
153
|
+
await assert.rejects(() => generate(parseArgs(data.args), { log() {}, warn() {} }));
|
|
154
|
+
await assert.rejects(() => readFile(path.join(rest, "QualityRuleController.java"), "utf8"), /ENOENT/);
|
|
155
|
+
assert.equal(await readFile(path.join(rest, "convertor"), "utf8"), "blocking-file\n");
|
|
60
156
|
});
|
|
@@ -84,7 +84,7 @@ Off the main flow entirely.
|
|
|
84
84
|
- **`/grilling`** — the interview primitive itself: rounds, the frontier, facts are the agent's job and decisions are yours. `/grill-me` and `/grill-with-docs` are the two named ways in, and `/triage`, `/wayfinder` and `/improve-codebase-architecture` all run it internally. Reach for it directly only when you want the interview with no wrapper around it.
|
|
85
85
|
- **`/resolving-merge-conflicts`** — work an in-progress merge or rebase conflict hunk by hunk, resolving by **intent** traced to each side's primary source rather than by picking lines, then finish the operation. It never runs `--abort`. Standalone and off every flow: reach for it when you are already mid-conflict.
|
|
86
86
|
- **`/prototype`** — a small, throwaway program that answers one design question: does this state model feel right, or what should this UI look like. Throwaway is a constraint on how the code is written, not a promise to destroy it: the answer folds into the real code, and the prototype itself is kept as a **primary source** on a `prototype/<name>` branch out of main, pointed at from the implementation issue. It's the detour in step 2 of the main flow, but reach for it any time a design question is hard to settle on paper.
|
|
87
|
-
- **`/research`** —
|
|
87
|
+
- **`/yss-research`** — investigate technical or strategy evidence using `technical-evidence` / `strategy-evidence` and `quick` / `evidence-audited` modes. Use a background agent when the runtime supports it and parallel reading is useful; delegation is not a trust guarantee. The old `/research` name is a deprecated alias. Research feeds `/grill-with-docs` and lifecycle decisions; it does not approve them.
|
|
88
88
|
- **`/to-questionnaire`** — when the thing blocking you isn't in your head or the codebase but in **someone else's**, this writes them a questionnaire to fill in. It's the inverse of `/grill-me`: instead of interviewing you about the subject, it interviews you about the **send** — who it's going to, what you need back — and aims the questions at the gap. What comes back is material for `/grill-with-docs` or `/to-spec`.
|
|
89
89
|
- **`/wait-what`** — the corrective for a message that didn't land. Use it mid-conversation, inside any other skill, and the agent re-pitches what it just said with the context you were missing, in plain English, using the `CONTEXT.md` vocabulary. It works after the fact; `/grill-with-docs` is the upfront cure, because a shared language agreed early is what stops the jargon arriving at all.
|
|
90
90
|
- **`/teach`** — learn a concept over multiple sessions, using the current directory as a stateful workspace.
|
|
@@ -74,7 +74,7 @@ The answer isn't part of the body — it's recorded on resolution (see [Work thr
|
|
|
74
74
|
|
|
75
75
|
Every ticket is either **HITL** — human in the loop, worked _with_ a human who speaks for themselves — or **AFK**, driven by the agent alone. A HITL ticket only resolves through that live exchange; the agent never stands in for the human's side of it (a grilling agent that answers its own questions has broken this).
|
|
76
76
|
|
|
77
|
-
- **Research** (AFK): Reading documentation, third-party APIs, or local resources like knowledge bases to surface a fact a decision waits on. Resolved by a `/research` **subagent
|
|
77
|
+
- **Research** (AFK): Reading documentation, third-party APIs, or local resources like knowledge bases to surface a fact a decision waits on. Resolved by a `/yss-research` **subagent** (`/research` is a deprecated alias). Use when knowledge outside the current working directory is required.
|
|
78
78
|
- **Prototype** (HITL): Raise the fidelity of the discussion by making a cheap, rough, concrete artifact to react to — an outline, a rough take, a stub, or UI/logic code via the /prototype skill. Links the prototype as an asset. Use when "how should it look" or "how should it behave" is the key question.
|
|
79
79
|
- **Grilling** (HITL): Conversation. The default case. Always invoke the /grilling and /domain-modeling skills.
|
|
80
80
|
- **Task** (HITL or AFK): Manual work that must happen before a _decision_ can be made — nothing to decide, prototype, or research, but the discussion is blocked until it's done. Signing up for a service so its API can be judged, provisioning access, moving data so its shape can be seen. This is the one type that _does_ rather than decides — and it earns its place by unblocking a decision, not by delivering the destination. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and any resulting facts (credentials location, new URLs, row counts) later tickets depend on.
|
|
@@ -112,7 +112,7 @@ User invokes with a loose idea.
|
|
|
112
112
|
2. **Map the frontier.** Grill again, **breadth-first** this time: fan out across the whole space rather than deep on any one thread, surfacing the open decisions and the first steps takeable now. **If this surfaces no fog** — the way to the destination is already clear, the whole journey small enough for one session — you don't need a map. Stop and ask the user how they'd like to proceed.
|
|
113
113
|
3. **Create the map** (label `wayfinder:map`): Destination and Notes filled in, Decisions-so-far empty, the fog sketched into **Not yet specified**.
|
|
114
114
|
4. **Create the tickets you can specify now** as child issues of the map — then wire blocking edges in a **second pass** (issues need ids before they can reference each other). Wiring sorts them into the frontier and the blocked; everything you can't yet specify stays in the fog — the **Not yet specified** section.
|
|
115
|
-
5. **Fire the research subagents.** For each `research` ticket you just created, spin up a `/research` subagent to resolve it in parallel, capturing its findings on a throwaway `research/<name>` branch with a context pointer from the ticket.
|
|
115
|
+
5. **Fire the research subagents.** For each `research` ticket you just created, spin up a `/yss-research` subagent to resolve it in parallel, capturing its findings on a throwaway `research/<name>` branch with a context pointer from the ticket. `/research` remains only as a deprecated compatibility alias.
|
|
116
116
|
6. Stop — charting is one session's work; it hand-resolves nothing.
|
|
117
117
|
|
|
118
118
|
### Work through the map
|
|
@@ -28,7 +28,7 @@ description: 用于生成完整的 YSS DDD 多模块后端脚手架。当用户
|
|
|
28
28
|
2. 优先运行 `node scripts/generate_and_verify_scaffold.mjs`,在同一个受控工作流中生成骨架并执行真实 Maven 验证。`generate_scaffold.mjs` 只是底层生成原语,单独返回 0 不代表脚手架完成。
|
|
29
29
|
3. 检查生成的模块名、POM、机械启动入口、基础配置文件和包路径。
|
|
30
30
|
4. 受控工作流必须在生成项目根目录实际执行 `./mvnw validate`、`./mvnw test` 和 `./mvnw package`;三条命令全部返回 0 后才能报告完成。
|
|
31
|
-
5. 三条 Wrapper 命令通过只得到 `empty-scaffold-verified
|
|
31
|
+
5. 三条 Wrapper 命令通过只得到 `empty-scaffold-verified`。如需声称已满足下游技能的首切片就绪条件,必须使用 `node scripts/run_first_slice_verification.mjs` 校验批准且版本当前的 Slice Implementation Contract、完整分层产物、skill tree digest 与根 Wrapper;只有验证器成功更新 Manifest 后才得到 `first-slice-verified`。
|
|
32
32
|
|
|
33
33
|
受控验证命令由本 skill 的 `node scripts/run_scaffold_verification.mjs` 固定执行;验证器先检查 `.yss/scaffold-generation.json` 的合同元数据和 Wrapper、Java、项目级 Maven settings/profile、仓库凭据是否就绪,再在指定 evidence 目录写入每条命令的 stdout/stderr、`exit_code`、`failure_category`、耗时、执行时间和 `scaffold-verification.json`。仓库或凭据失败归为 `repository-access`,与 `project-model`、`compilation`、`bootstrap-entrypoint`、`test-failure`、`packaging` 分开;任何一条命令失败或未执行都必须阻断。
|
|
34
34
|
|
|
@@ -55,6 +55,15 @@ node scripts/generate_and_verify_scaffold.mjs \
|
|
|
55
55
|
--evidence-dir /path/to/evidence/scaffold
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
完成批准的 golden first slice 后运行:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
node scripts/run_first_slice_verification.mjs \
|
|
62
|
+
--project-root /path/to/implementation-repo/my-service \
|
|
63
|
+
--slice-contract-file /path/to/approved-slice-contract.json \
|
|
64
|
+
--evidence-dir /path/to/evidence/first-slice
|
|
65
|
+
```
|
|
66
|
+
|
|
58
67
|
## 生成结果应包含
|
|
59
68
|
|
|
60
69
|
- 父工程 POM
|
|
@@ -75,7 +84,7 @@ node scripts/generate_and_verify_scaffold.mjs \
|
|
|
75
84
|
- Harness 内多项目布局必须使用 `apps/backend/<project>/`;`apps/backend/` 只能是生成器的父容器,`app/backend/`、`app/frontend/` 及其子路径一律拒绝。`git-submodule` gitlink、空挂载点、detached HEAD 工作树不得覆盖生成,即使传入 `--force` 也不得当成普通目录,且不得走「请显式传入 `--force`」普通目录覆盖 / rename 路径。`--output-dir` 指向 detached HEAD 子仓时不得 mkdir、staging 或生成工程。先 `git submodule update --init` 并在子仓附加分支工作树内生成。生成器必须先调用 `gitSubmoduleScaffoldViolation`,再在 exists / `--force` / rename 之前调用 `refuseGitlinkAsRegularDirectory`。
|
|
76
85
|
- 不要在 skill 里硬编码用户业务字段或真实连接信息。
|
|
77
86
|
- 生成后要检查依赖关系是否仍符合分层约束。
|
|
78
|
-
-
|
|
87
|
+
- 生成时的静态依赖由技能注册表与 Router 共同约束为 `yss-backend-scaffold-parent`、`alibaba-java-code-style`。生成后必须回到 Router,并按批准切片加载 `yss-domain`、`yss-application`、`yss-repository`、`yss-mybatis`、`yss-web-controller`、`yss-dto`、`yss-exception`、`yss-validation`、`mapstruct`、`lombok`、`alibaba-java-code-style` 等实际命中的行为 skill。
|
|
79
88
|
- 当前脚手架第一阶段仅支持经过验证的 `mysql`;未提供完整模板和验证的数据库类型不得伪装成已支持。
|
|
80
89
|
- 生成后的后端工程必须使用项目根目录 `./mvnw ...` 执行构建、测试、运行和 CI 验证;不得在 README、实施记录、Ticket、Review 或 Release 中默认写裸 `mvn ...`。既有仓库确实无法使用 wrapper 时,必须记录受控例外。
|
|
81
90
|
- 原型确认后,`scaffold_status=required` 才能进入本 skill;本 skill 的生成边界是工程结构、POM、配置、Wrapper 和机械模板,不是业务实现。
|
|
@@ -83,7 +92,8 @@ node scripts/generate_and_verify_scaffold.mjs \
|
|
|
83
92
|
- 新脚手架合同只接受 schema v2,必须分别携带 `project_name`、`base_package` 和 `maven_coordinates`;后者包含项目 `group_id` / `project_version`、父 POM GAV 与 `yss_components_version`,并由 CLI 原样传入。schema v1 为 `unsupported`,直接拒绝且不自动升级。
|
|
84
93
|
- 合同 `profiles` 只支持 `target-domain-model`、`mybatis-plus`、`mysql`、`spring-boot-2.7-jdk8`、`javax`、`web`、`yss-internal`。普通 MyBatis、Boot 3、独立 client module、client-in-domain 和其他旧架构均为 `unsupported`,不提供回退分支。
|
|
85
94
|
- 运行生成器必须传入 `--contract-file`;生成器会校验合同 `status=approved`、`current_version`、`primary_skill`、`controlled-generation`、实际输出路径和固定三条验证命令,不接受仅凭任意字符串引用的放行。
|
|
86
|
-
- 生成项目必须写入 Manifest v2 `.yss/scaffold-generation.json`,记录合同 digest、Target Profile、模板 digest
|
|
95
|
+
- 生成项目必须写入 Manifest v2 `.yss/scaffold-generation.json`,记录合同 digest、Target Profile、模板 digest、下游完整 skill tree digest、脚手架父合同与 Router 合同 digest、generator-owned 文件 hash、严格 `generation_policy` 和完成等级;清单缺失或不一致时不得交给后续 Router。
|
|
96
|
+
- `first-slice-verified` 只能由 `run_first_slice_verification.mjs` 写入。手工改 Manifest、只生成 Controller、只通过局部模块测试或仅有结构扫描都不能升级完成等级。
|
|
87
97
|
- 严禁把领域规则、状态机、权限、事务、复杂查询、错误映射、业务字段或用户可见行为塞进脚手架生成步骤。`./mvnw validate`、输出目录存在或“生成成功”都不等于生命周期批准、架构放行或 `ready-for-agent`。
|
|
88
98
|
- 生命周期脚手架生成必须关闭 `--with-example`,不得把 User CRUD 或业务字段当作样板。生成器严格 `initialize-only`:非空目标、`--force`、旧项目迁移和当前模板升级一律 `unsupported`。未来若支持同一 Target Profile 内的模板升级,必须另行设计和批准,当前不预留可执行承诺。
|
|
89
99
|
- 脚手架完成后,所有后续生成的后端代码必须回到 `yss-router`,消费批准且版本当前的 Slice Implementation Contract 和对应 YSS skill;业务行为使用 `behavior-tdd`,机械生成才使用 `controlled-generation`。
|
|
@@ -98,6 +108,7 @@ node scripts/generate_and_verify_scaffold.mjs \
|
|
|
98
108
|
- 主脚本:`scripts/generate_scaffold.mjs`
|
|
99
109
|
- 推荐的一键生成验证入口:`scripts/generate_and_verify_scaffold.mjs`
|
|
100
110
|
- 受控验证器:`scripts/run_scaffold_verification.mjs`
|
|
111
|
+
- 首切片验证器:`scripts/run_first_slice_verification.mjs`
|
|
101
112
|
- 模板目录:`assets/templates/`
|
|
102
113
|
- 分层细化参考:`references/yss-backend-scaffold-parent/` 及其子 skill
|
|
103
114
|
|
|
@@ -104,13 +104,13 @@ Domain Error -> Application propagation/translation -> Web Exception Translator
|
|
|
104
104
|
|
|
105
105
|
- Scaffold 只生成机械模块、POM、Wrapper、配置、架构规则和 Manifest,不生成生产业务 CRUD。
|
|
106
106
|
- 初始生成器为 `initialize-only`,非空目标失败。
|
|
107
|
-
- Manifest v2
|
|
107
|
+
- Manifest v2 记录模板、脚手架父合同、Router 合同、downstream 完整 skill tree digest 与 generator-owned 文件 hash。
|
|
108
108
|
- 当前不支持模板升级,不得重跑初始生成器覆盖业务工程。未来如需支持同一 Target Profile 内的模板版本升级,必须另行设计、批准并验证。
|
|
109
109
|
|
|
110
110
|
## 5. 完成等级
|
|
111
111
|
|
|
112
112
|
- `generated`:文件已原子生成,尚未完成 Wrapper 验证。
|
|
113
113
|
- `empty-scaffold-verified`:根目录 `./mvnw validate/test/package` 全部通过。
|
|
114
|
-
- `first-slice-verified
|
|
114
|
+
- `first-slice-verified`:批准且版本当前的 golden first slice 已具备 Domain 模型/Gateway、Application Service 实现/事务/Query Port、Repository/Mapper/GatewayImpl/QueryAdapter、DTO/Web/Exception 与分层测试;下游完整 skill tree digest 无漂移,且根 Wrapper 全部通过。该状态只能由 `scripts/run_first_slice_verification.mjs` 写入。
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
只有首切片验证器可以把 Manifest 从 `empty-scaffold-verified` 升级到最后一级;手工写值、局部测试或结构扫描均无效。只有最后一级可以声明已满足下游 YSS skills 的首切片就绪条件。
|
|
@@ -102,7 +102,18 @@ metadata-service/
|
|
|
102
102
|
|
|
103
103
|
## 4. 后续路由
|
|
104
104
|
|
|
105
|
-
`empty-scaffold-verified` 不等于业务切片可实现。后续必须回到 `yss-router`,消费批准且版本当前的 Slice Implementation Contract,再按影响面加载 `yss-domain`、`yss-application`、`yss-repository`、`yss-mybatis`、`yss-dto`、`yss-exception`、`yss-
|
|
105
|
+
`empty-scaffold-verified` 不等于业务切片可实现。后续必须回到 `yss-router`,消费批准且版本当前的 Slice Implementation Contract,再按影响面加载 `yss-domain`、`yss-application`、`yss-repository`、`yss-mybatis`、`yss-web-controller`、`yss-dto`、`yss-exception`、`yss-validation`、`mapstruct`、`lombok`、`alibaba-java-code-style` 等 skill,并使用 `behavior-tdd` 实现业务行为。
|
|
106
|
+
|
|
107
|
+
完成 golden first slice 后,必须运行:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
node .agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs \
|
|
111
|
+
--project-root /path/to/implementation-repo/metadata-service \
|
|
112
|
+
--slice-contract-file /path/to/approved-slice-contract.json \
|
|
113
|
+
--evidence-dir /path/to/evidence/first-slice
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
只有该验证器确认合同、全层产物、skill tree digest 与根 Wrapper 全部通过,并更新 Manifest 后,才能标记 `first-slice-verified`。
|
|
106
117
|
|
|
107
118
|
目标目录必须不存在。`--force`、旧项目迁移和当前模板升级均为 `unsupported`。旧项目继续按原工程维护;现代化改造必须单独立项、先评估再逐切片迁移。
|
|
108
119
|
|
|
@@ -45,7 +45,7 @@ infrastructure-+
|
|
|
45
45
|
- Maven 内部仓库通过命名 profile `yss-internal` 与环境变量注入;共享模板不写内部 URL 或凭据。
|
|
46
46
|
- Wrapper 必须带 checksum;DEBUG 和 MyBatis stdout SQL 只允许在 `application-local.yml`。
|
|
47
47
|
- `./mvnw validate`、`./mvnw test`、`./mvnw package` 全部成功只得到 `empty-scaffold-verified`。
|
|
48
|
-
-
|
|
48
|
+
- 只有批准且版本当前的 golden first slice 真实组合 Domain/Application/Infrastructure/Web、通过下游 skill tree drift 校验与根 Wrapper,才能由 `run_first_slice_verification.mjs` 写入 `first-slice-verified`。
|
|
49
49
|
|
|
50
50
|
## 下游权威技能
|
|
51
51
|
|
|
@@ -53,5 +53,6 @@ infrastructure-+
|
|
|
53
53
|
- Application:`yss-application`
|
|
54
54
|
- Persistence:`yss-repository` + `yss-mybatis`
|
|
55
55
|
- Web:`yss-web-controller` + `yss-dto` + `yss-exception`
|
|
56
|
+
- 横切实现约束:`yss-validation` + `mapstruct` + `lombok` + `alibaba-java-code-style`
|
|
56
57
|
|
|
57
58
|
业务代码必须回到 `yss-router`,消费批准且版本当前的 Slice Implementation Contract,并采用 `behavior-tdd`;脚手架只生成机械工程资产。
|