create-yss-spec 3.4.1 → 3.4.4
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 +2 -2
- package/template/.agents/skills/.strategic-design-skills-manifest.json +3 -3
- package/template/.agents/skills/archify/SKILL.md +1 -1
- package/template/.agents/skills/code-review/SKILL.md +12 -8
- package/template/.agents/skills/file-export-download/SKILL.md +1 -1
- package/template/.agents/skills/frontend-commit/SKILL.md +1 -1
- package/template/.agents/skills/java-backend-commit/SKILL.md +1 -1
- package/template/.agents/skills/llm-wiki/SKILL.md +1 -6
- package/template/.agents/skills/lombok/SKILL.md +10 -336
- package/template/.agents/skills/lombok/references/annotation-examples.md +307 -0
- package/template/.agents/skills/maintaining-skills/SKILL.md +2 -1
- package/template/.agents/skills/maintaining-skills/references/authoring.md +27 -0
- package/template/.agents/skills/mapstruct/SKILL.md +11 -318
- package/template/.agents/skills/mapstruct/references/annotation-examples.md +300 -0
- package/template/.agents/skills/page-form-module/SKILL.md +1 -1
- package/template/.agents/skills/page-skeleton/SKILL.md +1 -1
- package/template/.agents/skills/theme-token-usage/SKILL.md +1 -1
- package/template/.agents/skills/to-tickets/SKILL.md +1 -1
- package/template/.agents/skills/using-git-worktrees/SKILL.md +1 -1
- package/template/.agents/skills/wayfinder/SKILL.md +1 -1
- package/template/.agents/skills/yedit-table-usage/SKILL.md +1 -1
- package/template/.agents/skills/yss-api-integration/SKILL.md +1 -1
- package/template/.agents/skills/yss-audit-log/SKILL.md +1 -1
- package/template/.agents/skills/yss-cache/SKILL.md +1 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +16 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
- package/template/.agents/skills/yss-distributed-id/SKILL.md +1 -1
- package/template/.agents/skills/yss-dto/SKILL.md +5 -1
- package/template/.agents/skills/yss-excel-mvc/SKILL.md +1 -1
- package/template/.agents/skills/yss-exception/SKILL.md +5 -1
- package/template/.agents/skills/yss-formily-schema-generator/SKILL.md +1 -1
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +14 -6
- package/template/.agents/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
- package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
- package/template/.agents/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +1 -1
- package/template/.agents/skills/yss-openapi-governance/SKILL.md +1 -1
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +4 -4
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +29 -7
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +6 -2
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.agents/skills/yss-product-lifecycle/references/user-decisions.md +24 -4
- package/template/.agents/skills/yss-prototype-stage/SKILL.md +1 -1
- package/template/.agents/skills/yss-repository/SKILL.md +4 -0
- package/template/.agents/skills/yss-research/SKILL.md +1 -1
- package/template/.agents/skills/yss-resilience4j/SKILL.md +1 -1
- package/template/.agents/skills/yss-security-algorithm/SKILL.md +1 -1
- package/template/.agents/skills/yss-stage-decision/SKILL.md +1 -1
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +2 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +3 -3
- package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +3 -3
- package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +33 -0
- package/template/.agents/skills/yss-tactical-design/SKILL.md +5 -1
- package/template/.agents/skills/yss-technical-design/SKILL.md +5 -1
- package/template/.agents/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
- package/template/.agents/skills/yss-technical-design/tests/run-scenarios.mjs +1 -1
- package/template/.agents/skills/yss-ui/SKILL.md +1 -1
- package/template/.agents/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.agents/skills/yss-up-springboot3/SKILL.md +1 -1
- package/template/.agents/skills/yss-use-table-height/SKILL.md +1 -1
- package/template/.agents/skills/yss-use-tree-height/SKILL.md +1 -1
- package/template/.agents/skills/yss-userinfo/SKILL.md +1 -1
- package/template/.agents/skills/yss-validation/SKILL.md +5 -1
- package/template/.agents/skills/yss-web-controller/SKILL.md +5 -1
- package/template/.agents/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
- package/template/.agents/skills/ytable-usage/SKILL.md +1 -1
- package/template/.agents/skills/ytree-usage/SKILL.md +1 -1
- package/template/.codex/skills/archify/SKILL.md +1 -1
- package/template/.codex/skills/code-review/SKILL.md +12 -8
- package/template/.codex/skills/data-analytics/skills/analyze-data-quality/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/build-dashboard/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/build-report/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/design-kpis/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/gather-business-context/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/index/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/jupyter-notebooks/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/kpi-reporting/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/market-sizing/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/metric-diagnostics/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/product-business-analysis/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/spreadsheets/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/validate-data/SKILL.md +1 -1
- package/template/.codex/skills/data-analytics/skills/visualize-data/SKILL.md +1 -1
- package/template/.codex/skills/file-export-download/SKILL.md +1 -1
- package/template/.codex/skills/frontend-commit/SKILL.md +1 -1
- package/template/.codex/skills/java-backend-commit/SKILL.md +1 -1
- package/template/.codex/skills/llm-wiki/SKILL.md +1 -6
- package/template/.codex/skills/lombok/SKILL.md +10 -336
- package/template/.codex/skills/lombok/references/annotation-examples.md +307 -0
- package/template/.codex/skills/maintaining-skills/SKILL.md +2 -1
- package/template/.codex/skills/maintaining-skills/references/authoring.md +27 -0
- package/template/.codex/skills/mapstruct/SKILL.md +11 -318
- package/template/.codex/skills/mapstruct/references/annotation-examples.md +300 -0
- package/template/.codex/skills/page-form-module/SKILL.md +1 -1
- package/template/.codex/skills/page-skeleton/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/audit/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/design-qa/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/get-context/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/ideate/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/image-to-code/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/index/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/prototype/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/research/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/user-context/SKILL.md +1 -1
- package/template/.codex/skills/theme-token-usage/SKILL.md +1 -1
- package/template/.codex/skills/to-tickets/SKILL.md +1 -1
- package/template/.codex/skills/using-git-worktrees/SKILL.md +1 -1
- package/template/.codex/skills/wayfinder/SKILL.md +1 -1
- package/template/.codex/skills/yedit-table-usage/SKILL.md +1 -1
- package/template/.codex/skills/yss-api-integration/SKILL.md +1 -1
- package/template/.codex/skills/yss-audit-log/SKILL.md +1 -1
- package/template/.codex/skills/yss-cache/SKILL.md +1 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +16 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
- package/template/.codex/skills/yss-distributed-id/SKILL.md +1 -1
- package/template/.codex/skills/yss-dto/SKILL.md +5 -1
- package/template/.codex/skills/yss-excel-mvc/SKILL.md +1 -1
- package/template/.codex/skills/yss-exception/SKILL.md +5 -1
- package/template/.codex/skills/yss-formily-schema-generator/SKILL.md +1 -1
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +14 -6
- package/template/.codex/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
- package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
- package/template/.codex/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +1 -1
- package/template/.codex/skills/yss-openapi-governance/SKILL.md +1 -1
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +4 -4
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +29 -7
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +6 -2
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.codex/skills/yss-product-lifecycle/references/user-decisions.md +24 -4
- package/template/.codex/skills/yss-prototype-stage/SKILL.md +1 -1
- package/template/.codex/skills/yss-repository/SKILL.md +4 -0
- package/template/.codex/skills/yss-research/SKILL.md +1 -1
- package/template/.codex/skills/yss-resilience4j/SKILL.md +1 -1
- package/template/.codex/skills/yss-security-algorithm/SKILL.md +1 -1
- package/template/.codex/skills/yss-stage-decision/SKILL.md +1 -1
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +2 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +3 -3
- package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +3 -3
- package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +33 -0
- package/template/.codex/skills/yss-tactical-design/SKILL.md +5 -1
- package/template/.codex/skills/yss-technical-design/SKILL.md +5 -1
- package/template/.codex/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
- package/template/.codex/skills/yss-technical-design/tests/run-scenarios.mjs +1 -1
- package/template/.codex/skills/yss-ui/SKILL.md +1 -1
- package/template/.codex/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.codex/skills/yss-up-springboot3/SKILL.md +1 -1
- package/template/.codex/skills/yss-use-table-height/SKILL.md +1 -1
- package/template/.codex/skills/yss-use-tree-height/SKILL.md +1 -1
- package/template/.codex/skills/yss-userinfo/SKILL.md +1 -1
- package/template/.codex/skills/yss-validation/SKILL.md +5 -1
- package/template/.codex/skills/yss-web-controller/SKILL.md +5 -1
- package/template/.codex/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
- package/template/.codex/skills/ytable-usage/SKILL.md +1 -1
- package/template/.codex/skills/ytree-usage/SKILL.md +1 -1
- package/template/.cursor/skills/archify/SKILL.md +1 -1
- package/template/.cursor/skills/code-review/SKILL.md +12 -8
- package/template/.cursor/skills/file-export-download/SKILL.md +1 -1
- package/template/.cursor/skills/frontend-commit/SKILL.md +1 -1
- package/template/.cursor/skills/java-backend-commit/SKILL.md +1 -1
- package/template/.cursor/skills/llm-wiki/SKILL.md +1 -6
- package/template/.cursor/skills/lombok/SKILL.md +10 -336
- package/template/.cursor/skills/lombok/references/annotation-examples.md +307 -0
- package/template/.cursor/skills/maintaining-skills/SKILL.md +2 -1
- package/template/.cursor/skills/maintaining-skills/references/authoring.md +27 -0
- package/template/.cursor/skills/mapstruct/SKILL.md +11 -318
- package/template/.cursor/skills/mapstruct/references/annotation-examples.md +300 -0
- package/template/.cursor/skills/page-form-module/SKILL.md +1 -1
- package/template/.cursor/skills/page-skeleton/SKILL.md +1 -1
- package/template/.cursor/skills/theme-token-usage/SKILL.md +1 -1
- package/template/.cursor/skills/to-tickets/SKILL.md +1 -1
- package/template/.cursor/skills/using-git-worktrees/SKILL.md +1 -1
- package/template/.cursor/skills/wayfinder/SKILL.md +1 -1
- package/template/.cursor/skills/yedit-table-usage/SKILL.md +1 -1
- package/template/.cursor/skills/yss-api-integration/SKILL.md +1 -1
- package/template/.cursor/skills/yss-audit-log/SKILL.md +1 -1
- package/template/.cursor/skills/yss-cache/SKILL.md +1 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +16 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
- package/template/.cursor/skills/yss-distributed-id/SKILL.md +1 -1
- package/template/.cursor/skills/yss-dto/SKILL.md +5 -1
- package/template/.cursor/skills/yss-excel-mvc/SKILL.md +1 -1
- package/template/.cursor/skills/yss-exception/SKILL.md +5 -1
- package/template/.cursor/skills/yss-formily-schema-generator/SKILL.md +1 -1
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +14 -6
- package/template/.cursor/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
- package/template/.cursor/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +1 -1
- package/template/.cursor/skills/yss-openapi-governance/SKILL.md +1 -1
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +4 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +29 -7
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +6 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.cursor/skills/yss-product-lifecycle/references/user-decisions.md +24 -4
- package/template/.cursor/skills/yss-prototype-stage/SKILL.md +1 -1
- package/template/.cursor/skills/yss-repository/SKILL.md +4 -0
- package/template/.cursor/skills/yss-research/SKILL.md +1 -1
- package/template/.cursor/skills/yss-resilience4j/SKILL.md +1 -1
- package/template/.cursor/skills/yss-security-algorithm/SKILL.md +1 -1
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +1 -1
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +2 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +3 -3
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +3 -3
- package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +33 -0
- package/template/.cursor/skills/yss-tactical-design/SKILL.md +5 -1
- package/template/.cursor/skills/yss-technical-design/SKILL.md +5 -1
- package/template/.cursor/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
- package/template/.cursor/skills/yss-technical-design/tests/run-scenarios.mjs +1 -1
- package/template/.cursor/skills/yss-ui/SKILL.md +1 -1
- package/template/.cursor/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.cursor/skills/yss-up-springboot3/SKILL.md +1 -1
- package/template/.cursor/skills/yss-use-table-height/SKILL.md +1 -1
- package/template/.cursor/skills/yss-use-tree-height/SKILL.md +1 -1
- package/template/.cursor/skills/yss-userinfo/SKILL.md +1 -1
- package/template/.cursor/skills/yss-validation/SKILL.md +5 -1
- package/template/.cursor/skills/yss-web-controller/SKILL.md +5 -1
- package/template/.cursor/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
- package/template/.cursor/skills/ytable-usage/SKILL.md +1 -1
- package/template/.cursor/skills/ytree-usage/SKILL.md +1 -1
- package/template/.pi/skills/archify/SKILL.md +1 -1
- package/template/.pi/skills/code-review/SKILL.md +12 -8
- package/template/.pi/skills/file-export-download/SKILL.md +1 -1
- package/template/.pi/skills/frontend-commit/SKILL.md +1 -1
- package/template/.pi/skills/java-backend-commit/SKILL.md +1 -1
- package/template/.pi/skills/llm-wiki/SKILL.md +1 -6
- package/template/.pi/skills/lombok/SKILL.md +10 -336
- package/template/.pi/skills/lombok/references/annotation-examples.md +307 -0
- package/template/.pi/skills/maintaining-skills/SKILL.md +2 -1
- package/template/.pi/skills/maintaining-skills/references/authoring.md +27 -0
- package/template/.pi/skills/mapstruct/SKILL.md +11 -318
- package/template/.pi/skills/mapstruct/references/annotation-examples.md +300 -0
- package/template/.pi/skills/page-form-module/SKILL.md +1 -1
- package/template/.pi/skills/page-skeleton/SKILL.md +1 -1
- package/template/.pi/skills/theme-token-usage/SKILL.md +1 -1
- package/template/.pi/skills/to-tickets/SKILL.md +1 -1
- package/template/.pi/skills/using-git-worktrees/SKILL.md +1 -1
- package/template/.pi/skills/wayfinder/SKILL.md +1 -1
- package/template/.pi/skills/yedit-table-usage/SKILL.md +1 -1
- package/template/.pi/skills/yss-api-integration/SKILL.md +1 -1
- package/template/.pi/skills/yss-audit-log/SKILL.md +1 -1
- package/template/.pi/skills/yss-cache/SKILL.md +1 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +16 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/application-pom.xml.template +1 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/parent-pom.xml.template +15 -10
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/web-pom.xml.template +3 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +3 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +5 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +5 -2
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/generate_scaffold.mjs +13 -10
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +2 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +35 -3
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +1 -1
- package/template/.pi/skills/yss-distributed-id/SKILL.md +1 -1
- package/template/.pi/skills/yss-dto/SKILL.md +5 -1
- package/template/.pi/skills/yss-excel-mvc/SKILL.md +1 -1
- package/template/.pi/skills/yss-exception/SKILL.md +5 -1
- package/template/.pi/skills/yss-formily-schema-generator/SKILL.md +1 -1
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +14 -6
- package/template/.pi/skills/yss-implementation-contract-compiler/agents/openai.yaml +1 -1
- package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +16 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract-v2.md +214 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/references/slice-implementation-contract.md +66 -204
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/SKILL.md +15 -2
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +28 -19
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +10 -10
- package/template/.pi/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +1 -1
- package/template/.pi/skills/yss-openapi-governance/SKILL.md +1 -1
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +4 -4
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +29 -7
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +6 -2
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.pi/skills/yss-product-lifecycle/references/user-decisions.md +24 -4
- package/template/.pi/skills/yss-prototype-stage/SKILL.md +1 -1
- package/template/.pi/skills/yss-repository/SKILL.md +4 -0
- package/template/.pi/skills/yss-research/SKILL.md +1 -1
- package/template/.pi/skills/yss-resilience4j/SKILL.md +1 -1
- package/template/.pi/skills/yss-security-algorithm/SKILL.md +1 -1
- package/template/.pi/skills/yss-stage-decision/SKILL.md +1 -1
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +2 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +1 -1
- package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +3 -3
- package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +3 -3
- package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +33 -0
- package/template/.pi/skills/yss-tactical-design/SKILL.md +5 -1
- package/template/.pi/skills/yss-technical-design/SKILL.md +5 -1
- package/template/.pi/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +2 -2
- package/template/.pi/skills/yss-technical-design/tests/run-scenarios.mjs +1 -1
- package/template/.pi/skills/yss-ui/SKILL.md +1 -1
- package/template/.pi/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.pi/skills/yss-up-springboot3/SKILL.md +1 -1
- package/template/.pi/skills/yss-use-table-height/SKILL.md +1 -1
- package/template/.pi/skills/yss-use-tree-height/SKILL.md +1 -1
- package/template/.pi/skills/yss-userinfo/SKILL.md +1 -1
- package/template/.pi/skills/yss-validation/SKILL.md +5 -1
- package/template/.pi/skills/yss-web-controller/SKILL.md +5 -1
- package/template/.pi/skills/yss-web-controller/scripts/generate_controller.mjs +5 -2
- package/template/.pi/skills/ytable-usage/SKILL.md +1 -1
- package/template/.pi/skills/ytree-usage/SKILL.md +1 -1
- package/template/AGENTS.md +1 -0
- package/template/CONTEXT.md +2 -1
- package/template/docs/agents/backend-architecture-profiles.md +1 -1
- package/template/docs/agents/digital-human-roles.yaml +29 -1
- package/template/docs/agents/issue-tracker.md +2 -0
- package/template/docs/architecture/templates/engineering-baseline-review-template.md +6 -6
- package/template/docs/engineering/backend-platforms.json +174 -0
- package/template/docs/engineering/backend-platforms.md +77 -0
- package/template/docs/process/MATT-POCOCK-ENGINEERING-SKILLS.md +1 -0
- package/template/docs/process/existing-ui-baseline.md +1 -1
- package/template/docs/process/harness-process-tailoring.md +9 -1
- package/template/docs/process/lifecycle-artifact-map.md +6 -5
- package/template/docs/process/lifecycle-registry-baseline.json +2 -1
- package/template/docs/process/lifecycle-registry.yaml +17 -8
- package/template/docs/process/schemas/backend-architecture-identity.schema.json +91 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +3 -1
- package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +8 -0
- package/template/docs/process/schemas/project-scaffold-contract.schema.json +602 -83
- package/template/docs/process/schemas/review-bundle.schema.json +42 -0
- package/template/docs/process/schemas/scaffold-architecture-decisions.schema.json +323 -39
- package/template/docs/process/schemas/slice-implementation-contract-v3.schema.json +925 -0
- package/template/docs/process/strategic-handoff-package.md +5 -11
- package/template/docs/templates/approval-record-template.yaml +2 -0
- package/template/docs/templates/build-architecture-checklist-template.md +2 -2
- package/template/docs/templates/implementation-routing-template.md +4 -4
- package/template/docs/templates/review-bundle-template.yaml +26 -0
- package/template/docs/templates/vertical-slice-ticket-template.md +22 -31
- package/template/scripts/backend-platforms +14 -0
- package/template/scripts/lib/approval-record.mjs +52 -7
- package/template/scripts/lib/approved-execution-context.mjs +31 -8
- package/template/scripts/lib/backend-architecture.mjs +6 -0
- package/template/scripts/lib/backend-platform-provenance.mjs +67 -0
- package/template/scripts/lib/backend-platform-verification.mjs +210 -0
- package/template/scripts/lib/backend-platform.mjs +175 -0
- package/template/scripts/lib/backend-scaffold-prerequisites.mjs +1 -1
- package/template/scripts/lib/decision-continuation.mjs +79 -0
- package/template/scripts/lib/delivery-preflight.mjs +6 -5
- package/template/scripts/lib/digital-human-roles.mjs +10 -0
- package/template/scripts/lib/frontend-delivery-boundary.mjs +3 -0
- package/template/scripts/lib/implementation-contract-compiler.mjs +42 -9
- package/template/scripts/lib/lifecycle-controls.mjs +2 -2
- package/template/scripts/lib/lifecycle-transition.mjs +3 -1
- package/template/scripts/lib/plan-spec-entry.mjs +3 -3
- package/template/scripts/lib/scaffold-local-database.mjs +1 -0
- package/template/scripts/lib/slice-applicability.mjs +10 -0
- package/template/scripts/lib/slice-contract-preparation.mjs +257 -0
- package/template/scripts/lib/slice-contract-views.mjs +52 -0
- package/template/scripts/lib/slice-contract.mjs +211 -0
- package/template/scripts/lib/slice-repositories.mjs +49 -0
- package/template/scripts/lib/slice-task-package.mjs +66 -0
- package/template/scripts/lib/strategic-handoff-io.mjs +7 -0
- package/template/scripts/lib/strategic-handoff.mjs +33 -8
- package/template/scripts/lib/task-package.mjs +6 -2
- package/template/scripts/lib/technical-design-boundary.mjs +1 -0
- package/template/scripts/lib/user-decision-reuse.mjs +1 -0
- package/template/scripts/lib/user-decision.mjs +17 -3
- package/template/scripts/preflight-delivery +2 -2
- package/template/scripts/slice-contract +30 -0
- package/template/scripts/verify-backend-platform-matrix +69 -0
- package/template/scripts/verify-scaffold-architecture-decisions +5 -0
- package/template/scripts/verify-user-decision +7 -2
- package/template/skills-lock.json +62 -57
- package/template.snapshot.json +3 -3
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import {validateExistingUiBaseline} from './existing-ui-baseline.mjs';
|
|
2
|
+
import {sliceRepositories} from './slice-repositories.mjs';
|
|
3
|
+
import { sliceCheckApplicability } from './slice-applicability.mjs';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { parseDocument } from '../vendor/yaml.mjs';
|
|
7
|
+
import { safe, hash, digest, schema } from './strategic-handoff-io.mjs';
|
|
8
|
+
|
|
9
|
+
const originals = new WeakMap();
|
|
10
|
+
const sourceSnapshots = new WeakMap();
|
|
11
|
+
const nonempty = value => typeof value === 'string' && value.trim();
|
|
12
|
+
const unique = values => [...new Set(values)];
|
|
13
|
+
const fail = message => { throw new TypeError(`slice-contract-invalid: ${message}`); };
|
|
14
|
+
const requireThat = (ok, message) => { if (!ok) fail(message); };
|
|
15
|
+
export const sourceSliceContract = value => originals.get(value) || value?.slice_contract || value;
|
|
16
|
+
export function parseSliceYaml(bytes) {
|
|
17
|
+
const doc = parseDocument(String(bytes), { uniqueKeys: true, intAsBigInt: true });
|
|
18
|
+
requireThat(!doc.errors.length, `YAML 无效: ${doc.errors[0]?.message}`);
|
|
19
|
+
const value = doc.toJS({ maxAliasCount: 0 });
|
|
20
|
+
const convert = item => {
|
|
21
|
+
if (typeof item === 'bigint') {
|
|
22
|
+
requireThat(item <= BigInt(Number.MAX_SAFE_INTEGER) && item >= BigInt(Number.MIN_SAFE_INTEGER), 'YAML 整数超出精确范围,请使用字符串');
|
|
23
|
+
return Number(item);
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(item)) return item.map(convert);
|
|
26
|
+
if (item && typeof item === 'object') return Object.fromEntries(Object.entries(item).map(([k,v]) => [k,convert(v)]));
|
|
27
|
+
requireThat(typeof item !== 'number' || Number.isFinite(item), 'YAML 非有限数值');
|
|
28
|
+
return item;
|
|
29
|
+
};
|
|
30
|
+
return convert(value);
|
|
31
|
+
}
|
|
32
|
+
export function withinSlicePath(candidate, parent) {
|
|
33
|
+
if (!nonempty(candidate) || !nonempty(parent)) return false;
|
|
34
|
+
const rel = path.relative(path.resolve(parent), path.resolve(candidate));
|
|
35
|
+
return rel === '' || (!path.isAbsolute(rel) && rel !== '..' && !rel.startsWith(`..${path.sep}`));
|
|
36
|
+
}
|
|
37
|
+
function safeWritePath(value) {
|
|
38
|
+
requireThat(nonempty(value) && !path.isAbsolute(value) && !value.split(/[\\/]/).includes('..') && !/[\x00-\x1f*?{}\[\]]/.test(value), `写范围必须为明确的仓库相对路径: ${value}`);
|
|
39
|
+
}
|
|
40
|
+
export function resolveSliceBasis(contract, key, stack = []) {
|
|
41
|
+
requireThat(!stack.includes(key), `依据别名循环: ${[...stack,key].join(' -> ')}`);
|
|
42
|
+
const binding = contract.basis?.[key];
|
|
43
|
+
requireThat(binding !== undefined, `缺少上游依据 ${key}`);
|
|
44
|
+
return typeof binding === 'string' ? resolveSliceBasis(contract,binding,[...stack,key]) : binding;
|
|
45
|
+
}
|
|
46
|
+
export function readSliceSources(contract, { root = process.cwd() } = {}) {
|
|
47
|
+
const result = {}, bytesByPath=new Map();
|
|
48
|
+
for (const key of Object.keys(contract.basis)) {
|
|
49
|
+
const binding = resolveSliceBasis(contract,key);
|
|
50
|
+
const file=safe(root,binding.ref);
|
|
51
|
+
if(!bytesByPath.has(file))bytesByPath.set(file,fs.readFileSync(file));
|
|
52
|
+
const bytes=bytesByPath.get(file);
|
|
53
|
+
requireThat(hash(bytes) === binding.digest, `stale: 上游依据 ${key} 原始字节变化`);
|
|
54
|
+
result[key] = { ...binding, text: bytes.toString('utf8') };
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
export function sliceArchitectureEvidence(contract,sources) {
|
|
59
|
+
const identity=contract.resolution.architecture_identity;
|
|
60
|
+
if(!identity)return undefined;
|
|
61
|
+
const keys=['engineering_baseline','repository_registration','manifest'];
|
|
62
|
+
if(identity.schema_version!==2)return Object.fromEntries(keys.map(key=>[key,parseSliceYaml(sources[key].text)]));
|
|
63
|
+
const registration=parseSliceYaml(sources.repository_registration.text);
|
|
64
|
+
const evidence={repository_registration:resolveSliceBasis(contract,'repository_registration')};
|
|
65
|
+
for(const key of ['engineering_baseline','manifest']) {
|
|
66
|
+
const original=registration.architecture_evidence?.[key],bound=resolveSliceBasis(contract,key);
|
|
67
|
+
requireThat(original?.ref===bound.ref&&original?.digest===bound.digest,`登记与 ${key} 绑定冲突`);
|
|
68
|
+
evidence[key]=original;
|
|
69
|
+
}
|
|
70
|
+
return evidence;
|
|
71
|
+
}
|
|
72
|
+
function requireKeys(value, names, label) {
|
|
73
|
+
for (const name of names) requireThat(value[name] !== undefined, `${label} 缺少 ${name}`);
|
|
74
|
+
}
|
|
75
|
+
function locate(text, locator) {
|
|
76
|
+
if(locator.startsWith('pointer:')) {
|
|
77
|
+
const value=locator.slice(8).split('/').slice(1).reduce((current,key)=>current?.[key.replaceAll('~1','/').replaceAll('~0','~')],parseSliceYaml(text));
|
|
78
|
+
requireThat(value!==undefined,`定位缺失: ${locator}`);return JSON.stringify(value);
|
|
79
|
+
}
|
|
80
|
+
const lines = text.split('\n');
|
|
81
|
+
const range = /^lines:(\d+)-(\d+)$/.exec(locator);
|
|
82
|
+
if (range) {
|
|
83
|
+
const start=Number(range[1]),end=Number(range[2]);
|
|
84
|
+
requireThat(start>=1&&end>=start&&end<=lines.length,`定位范围无效: ${locator}`);
|
|
85
|
+
return lines.slice(start-1,end).join('\n');
|
|
86
|
+
}
|
|
87
|
+
const body=text.replace(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/,'');
|
|
88
|
+
const matches=body.split('\n').filter(line=>line.includes(locator));
|
|
89
|
+
requireThat(matches.length===1, `定位缺失或不唯一: ${locator}`);
|
|
90
|
+
return matches[0];
|
|
91
|
+
}
|
|
92
|
+
export function sliceAcceptanceText(contract, sources) {
|
|
93
|
+
return Object.fromEntries(Object.entries(contract.acceptance).map(([id,item])=>{
|
|
94
|
+
requireThat(sources[item.source],`验收 ${id} 来源缺失`);
|
|
95
|
+
return [id,locate(sources[item.source].text,item.locator)];
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
/** Validate the authoritative v3 document, then expand inherited facts ONLY in memory. */
|
|
99
|
+
export function normalizeSliceContract(document, options = {}) {
|
|
100
|
+
const raw = sourceSliceContract(document);
|
|
101
|
+
requireThat(raw && [2,3].includes(raw.schema_version), 'Slice Implementation Contract schema v1 已停止支持;需要 schema v2 或 v3');
|
|
102
|
+
if (raw.schema_version === 2) return structuredClone(raw);
|
|
103
|
+
schema(raw,'docs/process/schemas/slice-implementation-contract-v3.schema.json');
|
|
104
|
+
const sources=readSliceSources(raw,options), refs=Object.fromEntries(Object.entries(sources).map(([key,item])=>[key,item.ref]));
|
|
105
|
+
requireKeys(refs,['spec','ticket','engineering_baseline','implementation_repository','build_architecture_checklist'],'依据');
|
|
106
|
+
if(!raw.applicability.checks)requireKeys(refs,['architecture_review'],'依据');
|
|
107
|
+
else {
|
|
108
|
+
requireKeys(refs,['lifecycle_registry','process_tailoring'],'适用性依据');
|
|
109
|
+
const registry=parseSliceYaml(sources.lifecycle_registry.text),expected=sliceCheckApplicability(raw.scope,registry);
|
|
110
|
+
requireThat(digest(raw.applicability.checks)===digest(expected),'材料适用性与生命周期规则冲突');
|
|
111
|
+
if(expected['check.architecture-reviewed'].status==='required')requireKeys(refs,['architecture_review'],'命中检查');
|
|
112
|
+
}
|
|
113
|
+
if(raw.ticket_policy)requireThat(nonempty(sources.ticket.version),'冻结 Ticket 缺少需求版本');
|
|
114
|
+
const extension=raw.extensions || {}, scope=raw.scope, resolution=structuredClone(raw.resolution);
|
|
115
|
+
if(raw.ticket_policy&&scope.project_roots.length>1)requireThat(extension.cross_repo?.repository_bindings,'新跨仓草案缺少逐仓登记绑定');
|
|
116
|
+
const repositories=sliceRepositories(raw,sources,options);
|
|
117
|
+
for (const key of ['frontend','backend','api','cross_repo']) {
|
|
118
|
+
const item=raw.applicability[key];
|
|
119
|
+
if (item.status === 'required') requireThat(extension[key],`命中 ${key} 影响但缺少子合同`);
|
|
120
|
+
else {
|
|
121
|
+
requireThat(nonempty(item.reason),`${key} 不适用缺少理由`);
|
|
122
|
+
requireThat(!extension[key],`${key} 不适用时不得保存空子合同`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for(const [impact,section]of [['frontend','frontend'],['ui','frontend'],['backend','backend'],['api','api'],['cross-repo','cross_repo']])if(scope.impacted_areas.includes(impact))requireThat(raw.applicability[section].status==='required',`影响面 ${impact} 与适用性冲突`);
|
|
126
|
+
if(scope.impacted_areas.some(x=>['data','persistence'].includes(x)))requireKeys(refs,['data_architecture'],'数据依据');
|
|
127
|
+
if (extension.frontend && raw.applicability.frontend.baseline_kind==='existing-ui-baseline') {
|
|
128
|
+
requireThat(raw.applicability.frontend.ui_change==='none'&&!scope.impacted_areas.includes('ui'),'既有 UI 基线不能承接 UI 变化');
|
|
129
|
+
requireKeys(refs,['existing_ui_baseline','frontend_delivery'],'既有 UI 依据');
|
|
130
|
+
const observed=parseSliceYaml(sources.existing_ui_baseline.text);
|
|
131
|
+
const validation=validateExistingUiBaseline(observed,{bundleRoot:path.dirname(safe(options.root||process.cwd(),sources.existing_ui_baseline.ref))});
|
|
132
|
+
requireThat(validation.errors.length===0,`既有 UI 基线不可消费: ${validation.errors.join('; ')}`);
|
|
133
|
+
requireThat(extension.frontend.visual_baseline_case_ids.every(id=>observed.cases.some(c=>c.case_id===id)),'既有 UI 用例未被基线覆盖');
|
|
134
|
+
} else if (extension.frontend) requireKeys(refs,['requirement_freeze','low_fidelity_review','prototype_review','prototype_profile_decision','prototype_deliverable','prototype_deliverable_verification','prototype_confirmation','visual_baseline','state_matrix'],'前端依据');
|
|
135
|
+
if (extension.backend) {
|
|
136
|
+
requireKeys(refs,['technical_design','repository_registration','manifest','backend_repository','maven_wrapper'],'后端依据');
|
|
137
|
+
requireThat(resolution.architecture_identity||repositories&&Object.values(repositories).some(r=>r.resolution.architecture_identity),'后端缺少架构身份');
|
|
138
|
+
requireThat(nonempty(sources.technical_design.version),'技术设计缺少版本');
|
|
139
|
+
for (const selector of extension.backend.design_refs) locate(sources.technical_design.text,selector);
|
|
140
|
+
}
|
|
141
|
+
requireKeys(refs,[extension.api?'openapi_freeze':'no_api_impact_record'],'API 依据');
|
|
142
|
+
requireThat(!refs[extension.api?'no_api_impact_record':'openapi_freeze'],'API 影响结论冲突');
|
|
143
|
+
for (const p of scope.allowed_write_paths) safeWritePath(p);
|
|
144
|
+
requireThat(!scope.project_roots.some(p=>/^app\/(backend|frontend)(\/|$)/.test(p)), '禁止 app/backend 或 app/frontend');
|
|
145
|
+
if (scope.implementation_path_policy !== 'external-repository-native') for(const p of scope.project_roots) requireThat(/^apps\/(backend|frontend)\/[^/]+/.test(p),'Harness 工程必须落入具体 project 目录');
|
|
146
|
+
sliceAcceptanceText(raw,sources);
|
|
147
|
+
const unitIds=new Set(), usedChecks=new Set(), covered=new Set();
|
|
148
|
+
const expected=Object.keys(raw.acceptance);
|
|
149
|
+
for(const [id,check] of Object.entries(raw.verification)) {
|
|
150
|
+
requireThat(scope.project_roots.some(p=>withinSlicePath(check.cwd,p)),`验证 ${id} 的 cwd 未登记`);
|
|
151
|
+
if(check.dependency_roots)requireThat(repositories&&check.dependency_roots.every(p=>repositories[p]),`验证 ${id} 依赖仓库未登记`);
|
|
152
|
+
requireThat(check.acceptance_refs.length && check.acceptance_refs.every(ref=>expected.includes(ref)),`验证 ${id} 验收引用无效`);
|
|
153
|
+
}
|
|
154
|
+
const work_units=raw.work_units.map(unit=>{
|
|
155
|
+
requireThat(!unitIds.has(unit.id),`工作单元重复: ${unit.id}`);unitIds.add(unit.id);
|
|
156
|
+
const allowed=unit.allowed_write_paths || scope.allowed_write_paths;
|
|
157
|
+
for(const p of allowed) { safeWritePath(p);requireThat(scope.allowed_write_paths.some(parent=>withinSlicePath(p,parent)),`任务 ${unit.id} 写范围越界: ${p}`); }
|
|
158
|
+
const root=unit.project_root || (scope.project_roots.length===1?scope.project_roots[0]:null);
|
|
159
|
+
requireThat(scope.project_roots.includes(root),`任务 ${unit.id} 必须选择登记的 project_root`);
|
|
160
|
+
const checks=unique([...Object.keys(raw.verification).filter(id=>raw.verification[id].required_for_all),...unit.verification_refs]);
|
|
161
|
+
requireThat(checks.every(id=>raw.verification[id]),`任务 ${unit.id} 引用未知验证`);
|
|
162
|
+
requireThat(checks.every(id=>withinSlicePath(raw.verification[id].cwd,root)),`任务 ${unit.id} 验证执行仓库冲突`);
|
|
163
|
+
const skills=[unit.primary_skill,...(unit.supporting_skills || [])];
|
|
164
|
+
requireThat(skills.every(id=>resolution.required_skills.includes(id)),`任务 ${unit.id} Skill 不在冻结闭包内`);
|
|
165
|
+
requireThat(unit.acceptance_refs.every(id=>expected.includes(id)&&checks.some(k=>raw.verification[k].acceptance_refs.includes(id))),`任务 ${unit.id} 验收没有验证覆盖`);
|
|
166
|
+
for(const id of unit.acceptance_refs)covered.add(id);
|
|
167
|
+
for(const id of checks)usedChecks.add(id);
|
|
168
|
+
if(unit.tdd_mode==='controlled-generation') {
|
|
169
|
+
requireThat(unit.controlled_generation,'controlled-generation 缺少受控例外');
|
|
170
|
+
requireKeys(unit.controlled_generation,['exception_reason','generator','generator_inputs','expected_files','verification_commands','behavior_tests_after_generation'],'受控生成');
|
|
171
|
+
} else requireThat(!unit.controlled_generation,'行为测试不得携带受控生成例外');
|
|
172
|
+
const work_unit={...unit,allowed_write_paths:allowed,project_root:root,supporting_skills:unit.supporting_skills||[],verification:checks.map(id=>({id,...raw.verification[id]})),verification_commands:checks.map(id=>raw.verification[id].command),expected_evidence:unique(checks.flatMap(id=>raw.verification[id].expected_evidence)),forbidden_patterns:unique([...(scope.forbidden_patterns||[]),...(unit.forbidden_patterns||[])])};
|
|
173
|
+
return {...work_unit,contract_id:raw.contract_id,contract_version:raw.contract_version,work_unit};
|
|
174
|
+
});
|
|
175
|
+
requireThat(expected.every(id=>covered.has(id)), '存在未被工作单元覆盖的验收');
|
|
176
|
+
requireThat(Object.keys(raw.verification).every(id=>usedChecks.has(id)),'存在未分配的验证要求');
|
|
177
|
+
if(repositories)for(const id of extension.cross_repo.integration_verification)requireThat(raw.verification[id]?.dependency_roots?.length,'联合验证必须引用有依赖仓库和明确 cwd 的 verification');
|
|
178
|
+
const allChecks=Object.values(raw.verification);
|
|
179
|
+
resolution.freshness='current'; // Only after every bound source was read and verified above.
|
|
180
|
+
if(resolution.architecture_identity) {
|
|
181
|
+
resolution.architecture_identity_digest=digest(resolution.architecture_identity).slice(7);
|
|
182
|
+
resolution.architecture_evidence=sliceArchitectureEvidence(raw,sources);
|
|
183
|
+
}
|
|
184
|
+
if(refs.technical_design)resolution.technical_design=resolveSliceBasis(raw,'technical_design');
|
|
185
|
+
if(refs.frontend_delivery)resolution.frontend_delivery={acceptance_ref:refs.frontend_delivery,digest:sources.frontend_delivery.digest};
|
|
186
|
+
const common={...scope,required_capabilities:resolution.required_capabilities,required_skills:resolution.required_skills,verification_commands:allChecks.map(c=>c.command),expected_evidence_files:unique(allChecks.flatMap(c=>c.expected_evidence)),quality_baseline_ref:refs.engineering_baseline,forbidden_patterns:scope.forbidden_patterns||[],full_reroute_triggers:scope.full_reroute_triggers||[]};
|
|
187
|
+
const normalized={...structuredClone(raw),...(repositories?{repositories}:{}),common,resolution,lifecycle_refs:{...refs,openapi_freeze_or_no_impact:refs.openapi_freeze||refs.no_api_impact_record},readiness:{blockers:[],stale_inputs:[],not_applicable:Object.entries(raw.applicability).filter(([,a])=>a.status==='not-applicable').map(([item,a])=>({item,reason:a.reason}))},work_units,
|
|
188
|
+
frontend:extension.frontend?{status:'required',...extension.frontend,approved_prototype_ref:refs.prototype_confirmation,state_matrix_ref:refs.state_matrix,visual_baseline_ref:refs.visual_baseline,...(resolution.frontend_delivery?{delivery:resolution.frontend_delivery}:{})}:{status:'not-applicable'},
|
|
189
|
+
backend:extension.backend?{status:'required',...extension.backend}:{status:'not-applicable'},
|
|
190
|
+
contract:{api_impact:!!extension.api,...(extension.api?{freeze_ref:refs.openapi_freeze,...extension.api}:{no_api_impact_ref:refs.no_api_impact_record})},
|
|
191
|
+
cross_repo:extension.cross_repo?{repositories:scope.project_roots,...extension.cross_repo}:{}
|
|
192
|
+
};
|
|
193
|
+
originals.set(normalized,raw);
|
|
194
|
+
sourceSnapshots.set(normalized,sources);
|
|
195
|
+
return normalized;
|
|
196
|
+
}
|
|
197
|
+
export function readSliceContract(ref,{root=process.cwd(),diagnostic=false}={}) {
|
|
198
|
+
const bytes=fs.readFileSync(safe(root,ref)),raw=sourceSliceContract(parseSliceYaml(bytes));
|
|
199
|
+
const contract=diagnostic&&raw.status==='blocked'?null:normalizeSliceContract(raw,{root});
|
|
200
|
+
return {contract,raw,sources:structuredClone(sourceSnapshots.get(contract)||{}),binding:{ref,id:raw.contract_id,version:raw.contract_version,digest:hash(bytes)}};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Select only after full normalization; original raw identity is retained for approval comparison. */
|
|
204
|
+
export function selectSliceWorkUnit(contract,workUnitId) {
|
|
205
|
+
if(!contract.repositories)return contract;
|
|
206
|
+
const unit=contract.work_units.find(u=>u.id===workUnitId);
|
|
207
|
+
requireThat(unit,'跨仓执行必须选择唯一 work_unit_id');
|
|
208
|
+
const repo=contract.repositories[unit.project_root];
|
|
209
|
+
const selected={...contract,backend:repo.project.delivery_role==='backend'?contract.backend:{status:'not-applicable'},frontend:repo.project.delivery_role==='frontend'?{...contract.frontend,...(repo.resolution.frontend_delivery?{delivery:repo.resolution.frontend_delivery}:{})}:{status:'not-applicable'},resolution:{...repo.resolution,freshness:contract.resolution.freshness},common:{...contract.common,project_roots:[unit.project_root],allowed_write_paths:unit.allowed_write_paths},lifecycle_refs:{...contract.lifecycle_refs,...Object.fromEntries(Object.entries(repo.basis).map(([k,v])=>[k,v.ref]))}};
|
|
210
|
+
originals.set(selected,sourceSliceContract(contract));return selected;
|
|
211
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import * as lifecycle from './lifecycle-transition.mjs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {digest,schema} from './strategic-handoff-io.mjs';
|
|
5
|
+
import {parseSliceYaml,resolveSliceBasis,withinSlicePath,sliceArchitectureEvidence} from './slice-contract.mjs';
|
|
6
|
+
const check=(ok,message)=>{if(!ok)throw new TypeError(`slice-repository-invalid: ${message}`);};
|
|
7
|
+
/** All file facts stay in basis. Repository entries only select those facts. */
|
|
8
|
+
export function sliceRepositories(contract,sources,{root=process.cwd()}={}) {
|
|
9
|
+
const bindings=contract.extensions?.cross_repo?.repository_bindings;if(!bindings)return null;
|
|
10
|
+
check(lifecycle.SLICE_REPOSITORY_PREPARATION_PROTOCOL===1&&typeof lifecycle.validateImplementationRepositoriesReady==='function','接收端不支持当前来源的多项目准备协议,不能套用本地默认规则');
|
|
11
|
+
check(sources.repository_preparation,'缺少多项目准备结果');
|
|
12
|
+
const preparation=parseSliceYaml(sources.repository_preparation.text);
|
|
13
|
+
schema(preparation,'docs/process/schemas/implementation-repository-preparation-result.schema.json');
|
|
14
|
+
const ready=lifecycle.validateImplementationRepositoriesReady({implementation_repository_preparation:preparation,delivery_impacts:Object.fromEntries(['backend','frontend'].map(role=>[role,preparation.projects.some(p=>p.delivery_role===role&&p.status!=='not-applicable')]))},{root,exists:ref=>fs.existsSync(path.resolve(root,ref)),read:ref=>fs.readFileSync(path.resolve(root,ref),'utf8')});
|
|
15
|
+
check(ready.result!=='blocked',`多项目准备未就绪: ${ready.missing_requirements.join('; ')}`);
|
|
16
|
+
check(new Set(Object.keys(bindings).map(p=>path.resolve(p))).size===Object.keys(bindings).length,'工程根存在路径别名,不能重复登记');
|
|
17
|
+
const result={},identities=new Set();
|
|
18
|
+
for(const [projectRoot,keys]of Object.entries(bindings)) {
|
|
19
|
+
const references=Object.entries(keys).filter(([name])=>!['recipe_refs','capability_refs'].includes(name));
|
|
20
|
+
const selected=Object.fromEntries(references.map(([name,key])=>{check(sources[key],`缺少 basis.${key}`);return[name,sources[key]];}));
|
|
21
|
+
const registration=parseSliceYaml(selected.implementation_repository.text),baseline=parseSliceYaml(selected.engineering_baseline.text);
|
|
22
|
+
const actual=path.resolve(registration.local_worktree,registration.project_root||'.');
|
|
23
|
+
check(path.resolve(projectRoot)===actual,'工程根与逐仓登记冲突');
|
|
24
|
+
const project=preparation.projects.filter(p=>p.project_id===registration.project_id&&p.repository_ref===selected.implementation_repository.ref);
|
|
25
|
+
check(project.length===1&&project[0].status!=='not-applicable'&&path.resolve(project[0].project_root)===actual,'准备结果没有唯一当前工程');
|
|
26
|
+
const identityKey=JSON.stringify([registration.repository_id,registration.project_id]);check(!identities.has(identityKey),'同一仓库工程不能重复绑定');identities.add(identityKey);
|
|
27
|
+
check(registration.status==='current'&&baseline.status==='current','逐仓登记或基线非 current');
|
|
28
|
+
check(registration.repository_id===baseline.repository_id&®istration.project_id===baseline.project_id,'逐仓基线身份冲突');
|
|
29
|
+
check(Array.isArray(registration.allowed_write_paths)&®istration.allowed_write_paths.length,'登记缺少写边界');
|
|
30
|
+
if(project[0].delivery_role==='backend')check(selected.technical_design&&project[0].design_prerequisites.technical_design.digest===selected.technical_design.digest&&project[0].design_prerequisites.technical_design.ref===selected.technical_design.ref,'多项目准备与切片技术设计绑定冲突');
|
|
31
|
+
const identity=baseline.architecture_identity||registration.architecture_identity;
|
|
32
|
+
if(baseline.architecture_identity&®istration.architecture_identity)check(digest(baseline.architecture_identity)===digest(registration.architecture_identity),'逐仓架构冲突');
|
|
33
|
+
const basis=Object.fromEntries(references.map(([name,key])=>[name,resolveSliceBasis(contract,key)]));
|
|
34
|
+
basis.repository_registration??=basis.implementation_repository;
|
|
35
|
+
selected.repository_registration??=selected.implementation_repository;
|
|
36
|
+
const resolution={...contract.resolution};delete resolution.architecture_identity;
|
|
37
|
+
if(identity){resolution.architecture_identity=identity;resolution.architecture_identity_digest=digest(identity).slice(7);resolution.architecture_evidence=sliceArchitectureEvidence({basis,resolution},selected);}
|
|
38
|
+
if(basis.technical_design)resolution.technical_design=basis.technical_design;
|
|
39
|
+
if(basis.frontend_delivery)resolution.frontend_delivery={acceptance_ref:basis.frontend_delivery.ref,digest:basis.frontend_delivery.digest};
|
|
40
|
+
result[projectRoot]={registration,project:project[0],basis,resolution,recipe_refs:keys.recipe_refs,capability_refs:keys.capability_refs};
|
|
41
|
+
}
|
|
42
|
+
check(Object.keys(result).length===contract.scope.project_roots.length&&contract.scope.project_roots.every(p=>result[p]),'多仓绑定必须完整覆盖工程根');
|
|
43
|
+
for(const key of ['delivery_order','rollback_order'])check(contract.extensions.cross_repo[key].length===Object.keys(result).length&&contract.extensions.cross_repo[key].every(p=>result[p]),`${key} 必须完整且唯一列出工程根`);
|
|
44
|
+
for(const unit of contract.work_units) {
|
|
45
|
+
const repo=result[unit.project_root];check(repo,`工作单元 ${unit.id} 未选择唯一工程`);
|
|
46
|
+
check((unit.allowed_write_paths||contract.scope.allowed_write_paths).every(p=>repo.registration.allowed_write_paths.some(parent=>withinSlicePath(p,parent))),`工作单元 ${unit.id} 超出逐仓登记`);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {enforceHarnessTaskScope} from './harness-execution-scope.mjs';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { ROOT, hash, safe, digest } from './strategic-handoff-io.mjs';
|
|
4
|
+
import { normalizeSliceContract, withinSlicePath, parseSliceYaml } from './slice-contract.mjs';
|
|
5
|
+
import { createApprovedExecutionContext, assertApprovedExecutionContext } from './approved-execution-context.mjs';
|
|
6
|
+
import { assertImplementationDecision } from './user-decision.mjs';
|
|
7
|
+
import { taskPackageDefaults, loadDigitalHumanRoles } from './digital-human-roles.mjs';
|
|
8
|
+
import { validateTaskPackageSchema } from './task-package-schema.mjs';
|
|
9
|
+
const fail=message=>{throw new TypeError(message);};
|
|
10
|
+
const equalArrays=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((x,i)=>x===b[i]);
|
|
11
|
+
function generateTaskPackageDefaults(roleId,overrides,{rolesDoc}={}) {
|
|
12
|
+
const defaults=taskPackageDefaults(roleId,rolesDoc||loadDigitalHumanRoles());
|
|
13
|
+
return {schema_version:1,role_id:roleId,skill_source:{registry_ref:'docs/agents/digital-human-roles.yaml',defaults_ref:`taskPackageDefaults(${roleId})`,core_skills:defaults.core_skills,forbidden_skills:defaults.forbidden_skills},...overrides};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Compile dispatch from the approved YAML. Only runtime identity and task ID come from the caller. */
|
|
17
|
+
export function compileSliceTaskPackage(binding,{root=ROOT,work_unit_id,task_id,actor_id,runtime_id,execution_state='Worker',rolesDoc,user_decisions}={}) {
|
|
18
|
+
const actualRoles=parseSliceYaml(readFileSync(safe(root,'docs/agents/digital-human-roles.yaml')));
|
|
19
|
+
if(rolesDoc&&digest(rolesDoc)!==digest(actualRoles))fail('调用方不能替换接收端角色能力');
|
|
20
|
+
rolesDoc=actualRoles;
|
|
21
|
+
if(!actualRoles.runtimes.some(runtime=>runtime.id===runtime_id))fail('接收端不支持该 runtime_id');
|
|
22
|
+
const execution=createApprovedExecutionContext(binding,{root,work_unit_id});
|
|
23
|
+
const {contract}=assertApprovedExecutionContext(execution,{root});
|
|
24
|
+
if(contract.schema_version!==3)fail('v3 派发编译入口只接受 v3;v2 保留原派发路径');
|
|
25
|
+
const unit=contract.work_units.find(item=>item.id===work_unit_id);
|
|
26
|
+
if(!unit)fail('未找到指定 Slice 工作单元');
|
|
27
|
+
const checkpoint=readSliceDispatchApproval(root,binding.approval_ref);
|
|
28
|
+
const decisions=user_decisions||checkpoint.human_review?.user_decisions;
|
|
29
|
+
const output=generateTaskPackageDefaults(unit.role_id,{
|
|
30
|
+
task_id,work_unit_id,actor_id,runtime_id,execution_state,workflow_status:'not-started',stage_id:'stage.vertical-slice-implementation',
|
|
31
|
+
contract:{kind:'slice-implementation',contract_id:contract.contract_id,contract_version:contract.contract_version,status:'issued',contract_ref:binding.ref,slice_contract_ref:binding.ref,gate_refs:[binding.approval_ref]},
|
|
32
|
+
inputs:[binding.ref,...new Set(Object.values(contract.lifecycle_refs))],
|
|
33
|
+
objective:`${unit.behavior}(执行目录:${unit.project_root};完整约束读取 ${binding.ref})`,
|
|
34
|
+
allowed_write_paths:unit.allowed_write_paths,forbidden_actions:[...unit.work_unit.forbidden_patterns,'来源过期、越界、缺证据、验证失败或新增影响时停止并回交'],
|
|
35
|
+
expected_outputs:[unit.behavior],expected_evidence_files:unit.work_unit.expected_evidence,verification_commands:unit.work_unit.verification_commands,verification_results:[],
|
|
36
|
+
downstream_consumers:unit.downstream_consumers||['role.lifecycle-orchestrator'],convergence:{parent_work_unit:'work-unit.slice-implementation',convergence_ref:unit.convergence_ref||binding.approval_ref,conflict_escalation:'返回生命周期主控'},user_decisions:decisions
|
|
37
|
+
},{rolesDoc});
|
|
38
|
+
enforceHarnessTaskScope(output,{root});
|
|
39
|
+
validateTaskPackageSchema(output);
|
|
40
|
+
assertSliceV3TaskPackage(output,contract,{root});
|
|
41
|
+
return output;
|
|
42
|
+
}
|
|
43
|
+
function readSliceDispatchApproval(root,ref) {return parseSliceYaml(readFileSync(safe(root,ref)));}
|
|
44
|
+
export function assertSliceV3TaskPackage(value,slice,{root=ROOT}={}) {
|
|
45
|
+
enforceHarnessTaskScope(value,{root});
|
|
46
|
+
const contract=value.contract;
|
|
47
|
+
const current=normalizeSliceContract(slice,{root});
|
|
48
|
+
const roles=parseSliceYaml(readFileSync(safe(root,'docs/agents/digital-human-roles.yaml'))),defaults=taskPackageDefaults(value.role_id,roles);
|
|
49
|
+
if(!roles.runtimes.some(runtime=>runtime.id===value.runtime_id)||!defaults.stages.includes(value.stage_id))fail('接收端角色或运行时不支持当前任务');
|
|
50
|
+
if(!equalArrays(value.skill_source.core_skills,defaults.core_skills)||!equalArrays(value.skill_source.forbidden_skills,defaults.forbidden_skills))fail('任务角色能力与接收端不一致');
|
|
51
|
+
const unit=current.work_units.find(item=>item.id===value.work_unit_id);
|
|
52
|
+
if(unit&&[unit.primary_skill,...(unit.supporting_skills||[])].some(skill=>defaults.forbidden_skills.includes(skill)))fail('工作单元使用接收角色禁用 Skill');
|
|
53
|
+
if(!unit||unit.role_id!==value.role_id||current.contract_id!==contract.contract_id||current.contract_version!==contract.contract_version)fail('任务身份与 Slice v3 不一致');
|
|
54
|
+
if(!Array.isArray(contract.gate_refs)||contract.gate_refs.length!==1)fail('Slice v3 派发需要唯一批准 checkpoint 引用');
|
|
55
|
+
const bytes=readFileSync(safe(root,contract.slice_contract_ref));
|
|
56
|
+
const execution=createApprovedExecutionContext({ref:contract.slice_contract_ref,digest:hash(bytes),id:current.contract_id,version:current.contract_version,approval_ref:contract.gate_refs[0]},{root,work_unit_id:value.work_unit_id});
|
|
57
|
+
assertApprovedExecutionContext(execution,{root,contract:current});
|
|
58
|
+
for(const allowed of value.allowed_write_paths)if(!unit.allowed_write_paths.some(parent=>withinSlicePath(allowed,parent)))fail('任务写范围超出 Slice v3');
|
|
59
|
+
for(const key of ['verification_commands','expected_evidence_files']) {
|
|
60
|
+
const required=key==='verification_commands'?unit.work_unit.verification_commands:unit.work_unit.expected_evidence;
|
|
61
|
+
if(!equalArrays(value[key],required))fail(`任务包遗漏或替换 Slice v3 ${key}`);
|
|
62
|
+
}
|
|
63
|
+
for(const item of unit.work_unit.forbidden_patterns)if(!value.forbidden_actions.includes(item))fail('任务包遗漏全局约束');
|
|
64
|
+
if(value.execution_state==='Worker')assertImplementationDecision({slice_contract_ref:contract.slice_contract_ref,vertical_slice_ticket_ref:current.lifecycle_refs.ticket,user_decisions:value.user_decisions},{root});
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
@@ -78,6 +78,13 @@ export function sourceApprovalPolicy(source) {
|
|
|
78
78
|
roles.user_decision_policy ||= {gates:[]};
|
|
79
79
|
ensure(!(roles.user_decision_policy.required_capabilities || []).some(id => id !== 'strategic-decision-reuse-v1'), '接收工具不支持源用户决定策略,请升级工具后验包');
|
|
80
80
|
const policy=roles.gate_policy;
|
|
81
|
+
// Dedicated receivers may predate check_reviews. Mirror source check-review
|
|
82
|
+
// rules into their existing digital review lookup without changing source bytes.
|
|
83
|
+
for(const rule of policy.check_reviews || []) {
|
|
84
|
+
if(!(policy.digital_human_review||[]).some(x=>x.gate===rule.gate) && !(policy.dual_digital_human||[]).some(x=>x.gate===rule.gate)) {
|
|
85
|
+
policy.digital_human_review ||= [];policy.digital_human_review.push({gate:rule.gate,drafter:rule.drafter,countersigners:[...rule.countersigners]});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
81
88
|
for(const gate of policy.product_digital_human_with_biological_veto || []) {
|
|
82
89
|
if(!(policy.biological_human||[]).includes(gate) && !(policy.digital_human_review||[]).some(x=>x.gate===gate) && !(policy.dual_digital_human||[]).some(x=>x.gate===gate)) {
|
|
83
90
|
policy.digital_human_review ||= [];policy.digital_human_review.push({gate,countersigners:['role.product-manager']});
|
|
@@ -20,6 +20,19 @@ const SOURCE_SCHEMAS = new Map([
|
|
|
20
20
|
]);
|
|
21
21
|
const CAPABILITIES=['backend-technical-design','frontend-engineering-design','delivery-coordination'];
|
|
22
22
|
|
|
23
|
+
function sourceApprovalRecord(document, gateId) {
|
|
24
|
+
if(document?.kind!=='review-bundle')return document;
|
|
25
|
+
for(const field of ['bundle_id','task_id','work_unit_id','review_session_id','role_id','runtime_id','principal_ref'])ensure(nonempty(document[field]),`组合审查缺少 ${field}`);
|
|
26
|
+
ensure(Array.isArray(document.reviews)&&document.reviews.length,'组合审查不能为空');
|
|
27
|
+
ensure(new Set(document.reviews.map(row=>row?.gate_id)).size===document.reviews.length,'组合审查 gate_id 重复');
|
|
28
|
+
const rows=document.reviews.filter(row=>row.gate_id===gateId);
|
|
29
|
+
ensure(rows.length===1,`组合审查缺少当前结论: ${gateId}`);
|
|
30
|
+
const row=rows[0];
|
|
31
|
+
ensure(row.role_id===document.role_id&&row.runtime_id===document.runtime_id&&row.principal_ref===document.principal_ref,'组合审查必须来自同一复核角色、运行时和实例');
|
|
32
|
+
ensure(!row.review_session_id||row.review_session_id===document.review_session_id,'组合审查 review_session_id 不一致');
|
|
33
|
+
return {...row,review_bundle_id:document.bundle_id,review_task_id:document.task_id,review_work_unit_id:document.work_unit_id,review_session_id:document.review_session_id};
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
function deliveryReadme(handoff) {
|
|
24
37
|
const sources=Object.entries(handoff.source).sort(([a],[b])=>a.localeCompare(b)).map(([key,value])=>`- \`${key}\`: \`${value.persisted_ref}\` (${value.version}, ${value.digest})`);
|
|
25
38
|
const routes=handoff.consumer_routes.map(route=>`- \`${route.capability}\`: \`${route.activation}\` (${route.route_id})`);
|
|
@@ -75,6 +88,17 @@ export async function sourceApproval(record, roles, root) {
|
|
|
75
88
|
const { assertApprovalUserDecision } = await import('./user-decision-reuse.mjs');
|
|
76
89
|
assertApprovalUserDecision(record, roles, options);
|
|
77
90
|
}
|
|
91
|
+
// Older dedicated receivers retain their own lifecycle policy. Normalize only
|
|
92
|
+
// this already-verified source approval, without granting local capabilities.
|
|
93
|
+
if (record.continuation_ref) {
|
|
94
|
+
ensure(roles.user_decision_policy.gates.includes(record.gate_id), '未登记的源延续批准边界');
|
|
95
|
+
const reviewers = roles.gate_policy.continuation_reviews?.[record.gate_id];
|
|
96
|
+
ensure(reviewers?.includes(record.role_id) && record.drafter_principal_ref && record.drafter_principal_ref !== record.principal_ref, '源授权延续缺少独立审查身份');
|
|
97
|
+
options.rolesDoc = structuredClone(roles);
|
|
98
|
+
const policy = options.rolesDoc.gate_policy;
|
|
99
|
+
policy.dual_digital_human = (policy.dual_digital_human || []).filter(x => x.gate !== record.gate_id);
|
|
100
|
+
policy.digital_human_review = [...(policy.digital_human_review || []).filter(x => x.gate !== record.gate_id), { gate: record.gate_id, countersigners: reviewers }];
|
|
101
|
+
}
|
|
78
102
|
validateApprovalRecord(record,options);
|
|
79
103
|
}
|
|
80
104
|
|
|
@@ -160,13 +184,14 @@ export async function inspectSource(root, handoffRef) {
|
|
|
160
184
|
const indexes=extractTraceability(strategy);
|
|
161
185
|
const sourceRoles=read(safe(root,'docs/agents/digital-human-roles.yaml'));
|
|
162
186
|
const roles=sourceApprovalPolicy(sourceRoles);
|
|
163
|
-
for (const [artifact, version] of [[strategy,strategy.domain_version],[stage,stage.package_version]]) {
|
|
164
|
-
if(artifact.approval) { const record=read(safe(root,artifact.approval.approval_ref));await sourceApproval(record,roles,root);ensure(artifact.approval.current_version===version,'资产内置批准版本过期'); }
|
|
187
|
+
for (const [artifact, version, gateId] of [[strategy,strategy.domain_version,'check.domain-strategy-approved'],[stage,stage.package_version,'check.stage-decision-package-approved']]) {
|
|
188
|
+
if(artifact.approval) { const record=sourceApprovalRecord(read(safe(root,artifact.approval.approval_ref)),gateId);await sourceApproval(record,roles,root);ensure(artifact.approval.current_version===version,'资产内置批准版本过期'); }
|
|
165
189
|
}
|
|
166
190
|
// Source packages retain their published approval vocabulary. Never promote old approvals into current checkpoint gates.
|
|
167
191
|
const currentPlan = (roles.gate_policy.dual_digital_human || []).some(rule => rule.gate === 'gate.plan-approved');
|
|
168
|
-
const
|
|
169
|
-
const
|
|
192
|
+
const explicitlyReviewed=(gate)=>(roles.gate_policy.digital_human_review||[]).some(rule=>rule.gate===gate)||(roles.gate_policy.dual_digital_human||[]).some(rule=>rule.gate===gate)||(roles.gate_policy.biological_human||[]).includes(gate);
|
|
193
|
+
const terminalGate=currentPlan||explicitlyReviewed('gate.strategic-design-handoff-approved')?'gate.strategic-design-handoff-approved':'gate.stage-decision-package-approved';
|
|
194
|
+
const expectedGates={domain_strategy_ref:currentPlan?'gate.plan-approved':'gate.domain-strategy-approved',stage_decision_package_ref:currentPlan?'gate.plan-approved':'gate.stage-decision-package-approved',spec_ref:'gate.spec-baseline-approved',prototype_ref:currentPlan?'gate.product-design-approved':'gate.user-confirmation',visual_baseline_ref:currentPlan?'gate.product-design-approved':'gate.user-confirmation',business_ticket_set_ref:currentPlan?'gate.plan-approved':terminalGate,handoff:terminalGate};
|
|
170
195
|
expectedGates.existing_ui_baseline_ref=expectedGates.prototype_ref;
|
|
171
196
|
const bindings={...handoff.source,handoff:{id:handoff.handoff_id,version:handoff.handoff_version,persisted_ref:handoffRef,status:'approved'}};
|
|
172
197
|
for(const [key,ref] of Object.entries(bindings)) {
|
|
@@ -183,14 +208,14 @@ export async function inspectSource(root, handoffRef) {
|
|
|
183
208
|
}
|
|
184
209
|
} else actual=bytesDigest(readFileSync(safe(root,ref.persisted_ref)),approval.digest_kind);
|
|
185
210
|
if(key!=='handoff')ensure(ref.digest===actual,`源资产摘要过期: ${key}`);
|
|
186
|
-
const record=read(safe(root,approval.record_ref));
|
|
211
|
+
const record=sourceApprovalRecord(read(safe(root,approval.record_ref)),approval.gate_id);
|
|
187
212
|
if(key==='existing_ui_baseline_ref')ensure(record.subject_ref===`${ref.persisted_ref}/${ref.manifest_ref}`,'既有 UI 用户决定必须以当前 manifest 为批准主体');
|
|
188
213
|
await sourceApproval(record,roles,root);
|
|
189
214
|
ensure(record.gate_id===approval.gate_id,`批准门禁不匹配: ${key}`);
|
|
190
215
|
ensure((record.artifact_bindings || []).some(x=>x.id===(ref.id||ref.baseline_id) && x.version===ref.version && x.digest===actual),`批准记录未绑定当前资产: ${key}`);
|
|
191
216
|
}
|
|
192
217
|
if (roles.user_decision_policy.required_capabilities?.includes('strategic-decision-reuse-v1')) {
|
|
193
|
-
const record=read(safe(root,config.approvals.handoff.record_ref));
|
|
218
|
+
const record=sourceApprovalRecord(read(safe(root,config.approvals.handoff.record_ref)),config.approvals.handoff.gate_id);
|
|
194
219
|
const scope=read(safe(root,record.subject_ref));
|
|
195
220
|
ensure(scope.kind==='strategic-delivery-scope' && scope.schema_version===1, '交接必须绑定独立的交付范围清单');
|
|
196
221
|
const {package_export,status,...delivery}=handoff;
|
|
@@ -198,7 +223,7 @@ export async function inspectSource(root, handoffRef) {
|
|
|
198
223
|
ensure(scope.assets?.some(asset=>asset.ref===scope.delivery_ref && asset.digest===hash(readFileSync(safe(root,scope.delivery_ref)))), '交接内容快照缺少批准覆盖');
|
|
199
224
|
for (const [key,ref] of Object.entries(handoff.source)) {
|
|
200
225
|
const file=['visual_baseline_ref','existing_ui_baseline_ref'].includes(key)?`${ref.persisted_ref}/${ref.manifest_ref}`:ref.persisted_ref;
|
|
201
|
-
const decisionBoundaries=
|
|
226
|
+
const decisionBoundaries=[expectedGates[key]];
|
|
202
227
|
ensure(scope.assets?.some(asset=>asset.ref===file && asset.version===ref.version && decisionBoundaries.includes(asset.boundary) && asset.digest===hash(readFileSync(safe(root,file)))), `交付范围清单遗漏当前资产: ${key}`);
|
|
203
228
|
}
|
|
204
229
|
}
|
|
@@ -217,7 +242,7 @@ function collect(root, handoffRef, handoff, config) {
|
|
|
217
242
|
const enqueue=ref=> { if(symbolic[ref])queue.push(symbolic[ref]); else if(/^https?:\/\//i.test(ref))return; else if(ref.startsWith('evidence.'))throw new TypeError(`未解析 evidence ID: ${ref}`); else if(ref.includes('/') || /\.(?:md|yaml|json|html|png|txt|log)$/.test(ref))queue.push(ref); };
|
|
218
243
|
function refs(value,key='') {
|
|
219
244
|
if(Array.isArray(value)) { if(key==='evidence_refs') value.forEach(enqueue); else value.forEach(v=>refs(v,key)); }
|
|
220
|
-
else if(value && typeof value==='object') for(const [k,v]of Object.entries(value)) { if(['approval_ref','persisted_ref','user_decision_ref','decision_reuse_ref','scope_ref','subject_ref','delivery_ref'].includes(k)&&typeof v==='string')enqueue(v); else if(k==='ref'&&typeof v==='string'&&!/^[a-z]+:\/\//i.test(v))enqueue(v); else refs(v,k); }
|
|
245
|
+
else if(value && typeof value==='object') for(const [k,v]of Object.entries(value)) { if(['approval_ref','persisted_ref','user_decision_ref','decision_reuse_ref','continuation_ref','plan_continuation_ref','scope_ref','subject_ref','delivery_ref'].includes(k)&&typeof v==='string')enqueue(v); else if(k==='ref'&&typeof v==='string'&&!/^[a-z]+:\/\//i.test(v))enqueue(v); else refs(v,k); }
|
|
221
246
|
}
|
|
222
247
|
let totalBytes = 0;
|
|
223
248
|
while(queue.length) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { normalizeSliceContract } from './slice-contract.mjs';
|
|
2
|
+
import { assertSliceV3TaskPackage } from './slice-task-package.mjs';
|
|
3
|
+
export { compileSliceTaskPackage, assertSliceV3TaskPackage } from './slice-task-package.mjs';
|
|
1
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
5
|
import path from "node:path";
|
|
3
6
|
import { parseDocument } from "../vendor/yaml.mjs";
|
|
@@ -178,11 +181,12 @@ function validateContract(value, registry, lifecycle) {
|
|
|
178
181
|
const contractPath = assertSafeRelativePath(contract.slice_contract_ref, "contract.slice_contract_ref");
|
|
179
182
|
if (!existsSync(contractPath)) fail(`Slice Implementation Contract 不存在: ${contract.slice_contract_ref}`);
|
|
180
183
|
const document = parseYaml(readFileSync(contractPath, "utf8"), "Slice Implementation Contract");
|
|
181
|
-
const slice = document
|
|
182
|
-
if (slice.schema_version
|
|
184
|
+
const slice = normalizeSliceContract(document,{root:ROOT});
|
|
185
|
+
if (![2,3].includes(slice.schema_version) || typeof slice.slice_id !== "string" || !slice.lifecycle_refs || !slice.readiness || !Array.isArray(slice.work_units)) fail("Slice Implementation Contract 需要 schema v2 及 slice_id/lifecycle_refs/readiness/work_units");
|
|
183
186
|
if (slice.contract_id !== contract.contract_id || slice.contract_version !== contract.contract_version || slice.status !== "approved") fail("Slice Contract 必须是当前 approved 版本");
|
|
184
187
|
const sliceUnit = slice.work_units.find((item) => item && item.id === value.work_unit_id);
|
|
185
188
|
if (!sliceUnit) fail(`Slice Implementation Contract 缺少 work_unit_id: ${value.work_unit_id}`);
|
|
189
|
+
if(slice.schema_version===3)return assertSliceV3TaskPackage(value,slice,{root:ROOT});
|
|
186
190
|
for (const field of ["role_id", "runtime_id", "task_package_ref", "contract_id", "contract_version", "allowed_write_paths"]) if (sliceUnit[field] === undefined) fail(`Slice Contract work_unit 缺少 ${field}`);
|
|
187
191
|
if (sliceUnit.role_id !== value.role_id || sliceUnit.runtime_id !== value.runtime_id || sliceUnit.contract_id !== contract.contract_id || sliceUnit.contract_version !== contract.contract_version) fail("任务包与 Slice Contract work_unit 不一致");
|
|
188
192
|
for (const allowed of value.allowed_write_paths) if (!sliceUnit.allowed_write_paths.includes(allowed)) fail(`allowed_write_paths 未获 Slice Contract 授权: ${allowed}`);
|
|
@@ -24,6 +24,7 @@ export function enforceTechnicalDesign(state = {}, { root = ROOT, execution, rea
|
|
|
24
24
|
if(execution){
|
|
25
25
|
assertApprovedExecutionContext(execution,{root,technicalDesign:data,architectureIdentity:state.architecture_identity,sliceId:state.slice_id});
|
|
26
26
|
const approved=approvedExecutionBinding(execution);
|
|
27
|
+
if(approved.work_unit_id)args.push('--work-unit',approved.work_unit_id);
|
|
27
28
|
args.push('--approved-slice',approved.ref,'--approved-slice-digest',approved.digest,'--approved-slice-approval',approved.approval_ref);
|
|
28
29
|
}
|
|
29
30
|
const verified = spawnSync(process.execPath, args, { cwd: root, encoding: 'utf8', timeout: 60000, maxBuffer: 2 * 1024 * 1024 });
|
|
@@ -8,6 +8,7 @@ const sameSet = (a,b) => Array.isArray(a) && Array.isArray(b) && a.length === ne
|
|
|
8
8
|
export function assertApprovalUserDecision(record, roles, options = {}) {
|
|
9
9
|
const policy = roles.user_decision_policy;
|
|
10
10
|
const io = decisionIO(options);
|
|
11
|
+
if (record.continuation_ref) return assertUserDecisionRequirement({boundary:record.gate_id,subject_ref:record.subject_ref,scope:record.approval_scope,continuation_ref:record.continuation_ref}, {...options,rolesDoc:roles});
|
|
11
12
|
if (policy.required_capabilities?.some(id => id !== DECISION_REUSE_CAPABILITY)) fail('接收工具不支持源用户决定策略,请升级');
|
|
12
13
|
if (!record.decision_reuse_ref) return assertUserDecisionRequirement({boundary:record.gate_id,subject_ref:record.subject_ref,scope:record.approval_scope,user_decision_ref:record.user_decision_ref}, {...options,rolesDoc:roles});
|
|
13
14
|
if (record.gate_id !== 'gate.strategic-design-handoff-approved' || !policy.required_capabilities?.includes(DECISION_REUSE_CAPABILITY)) fail('未登记的复用边界或能力');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeSliceContract } from './slice-contract.mjs';
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import { readFileSync } from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
@@ -5,6 +6,8 @@ import { parseDocument } from "../vendor/yaml.mjs";
|
|
|
5
6
|
import { ROOT } from "./lifecycle-registry.mjs";
|
|
6
7
|
import { loadDigitalHumanRoles } from "./digital-human-roles.mjs";
|
|
7
8
|
import { validateJsonSchema } from "./json-schema.mjs";
|
|
9
|
+
import { assertDecisionContinuation } from './decision-continuation.mjs';
|
|
10
|
+
import { platformProfile } from './backend-platform.mjs';
|
|
8
11
|
|
|
9
12
|
export function decisionError(code, detail) {
|
|
10
13
|
const error = new TypeError(`${code}: ${detail}`);
|
|
@@ -161,6 +164,7 @@ export function verifyUserDecisionFile(ref, options = {}) {
|
|
|
161
164
|
|
|
162
165
|
export function assertUserDecisionRequirement(requirement, options = {}) {
|
|
163
166
|
if (!requirement) decisionError("user-decision-response-required", "缺少当前决定及其范围");
|
|
167
|
+
if (requirement.continuation_ref) return assertDecisionContinuation(requirement, options.rolesDoc || loadDigitalHumanRoles(), options);
|
|
164
168
|
return verifyUserDecisionFile(requirement.user_decision_ref, { ...options, expected: [requirement] });
|
|
165
169
|
}
|
|
166
170
|
|
|
@@ -191,7 +195,7 @@ export function assertImplementationDecision(state, options = {}) {
|
|
|
191
195
|
if (state.slice_contract_ref !== slice.contract?.ref) decisionError("user-decision-subject-mismatch", "当前合同不在批准清单中");
|
|
192
196
|
checkAsset(slice.contract, io);
|
|
193
197
|
const content = io.document(slice.contract.ref);
|
|
194
|
-
const contract = content.slice_contract || content;
|
|
198
|
+
const contract = content.slice_contract?.schema_version === 3 || content.schema_version === 3 ? normalizeSliceContract(content, { root: options.root || ROOT }) : content.slice_contract || content;
|
|
195
199
|
if (contract.lifecycle_refs?.ticket !== ticketRef) decisionError("user-decision-subject-mismatch", "持久化合同的切片引用不匹配");
|
|
196
200
|
for (const [manifestKey, contractKey] of [["repositories", "project_roots"], ["allowed_write_paths", "allowed_write_paths"]]) {
|
|
197
201
|
if (!Array.isArray(slice[manifestKey]) || slice[manifestKey].length === 0 || JSON.stringify([...slice[manifestKey]].sort()) !== JSON.stringify([...(contract.common?.[contractKey] || [])].sort())) decisionError("user-decision-scope-mismatch", manifestKey);
|
|
@@ -202,11 +206,21 @@ export function assertImplementationDecision(state, options = {}) {
|
|
|
202
206
|
|
|
203
207
|
export function scaffoldDecisionSnapshot(decision) {
|
|
204
208
|
const { user_confirmation, status, decision_inputs_digest, ...inputs } = decision;
|
|
205
|
-
|
|
209
|
+
const profile = platformProfile(decision.platform_profile, undefined, decision.platform_configuration?.spring_boot_version);
|
|
210
|
+
return {
|
|
211
|
+
...inputs,
|
|
212
|
+
platform_selection: {
|
|
213
|
+
profile_id: profile.id,
|
|
214
|
+
spring_boot_version: profile.spring_boot_version,
|
|
215
|
+
java_version: profile.java_version,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
206
218
|
}
|
|
207
219
|
export function assertScaffoldUserDecision(decision, options = {}) {
|
|
208
220
|
const confirmation = decision.user_confirmation || {};
|
|
209
|
-
const
|
|
221
|
+
const profile = platformProfile(decision.platform_profile, undefined, decision.platform_configuration?.spring_boot_version);
|
|
222
|
+
const platformScope = `${profile.id}@${profile.spring_boot_version}/java${profile.java_version}`;
|
|
223
|
+
const requirement = { boundary: "gate.backend-architecture-platform-approved", subject_ref: confirmation.decision_subject_ref, scope: [decision.project_id, decision.confirmed_architecture, platformScope, decision.decision_inputs_digest], user_decision_ref: confirmation.user_decision_ref };
|
|
210
224
|
assertUserDecisionRequirement(requirement, options);
|
|
211
225
|
const io = decisionIO(options);
|
|
212
226
|
if (decisionDigest(io.document(requirement.subject_ref)) !== decisionDigest(scaffoldDecisionSnapshot(decision))) decisionError("user-decision-stale", "脚手架输入与用户所见快照不一致");
|
|
@@ -6,9 +6,9 @@ import { preflightDelivery, invalidPreflightInput } from './lib/delivery-preflig
|
|
|
6
6
|
|
|
7
7
|
let result;
|
|
8
8
|
try {
|
|
9
|
-
const {values}=parseArgs({options:{input:{type:'string'},stage:{type:'string'},json:{type:'boolean'}},strict:true});
|
|
9
|
+
const {values}=parseArgs({options:{input:{type:'string'},stage:{type:'string'},'work-unit':{type:'string'},json:{type:'boolean'}},strict:true});
|
|
10
10
|
if(!values.input||!values.stage) throw new TypeError('必须提供 --input <file> 和 --stage prepare|build|export|accept');
|
|
11
|
-
result=await preflightDelivery(read(path.resolve(values.input)),{stage:values.stage,root:process.cwd()});
|
|
11
|
+
result=await preflightDelivery(read(path.resolve(values.input)),{stage:values.stage,root:process.cwd(),work_unit_id:values['work-unit']});
|
|
12
12
|
} catch(error) { result=invalidPreflightInput(error.message); }
|
|
13
13
|
process.stdout.write(`${JSON.stringify(result,null,2)}\n`);
|
|
14
14
|
process.exitCode=result.exit_code;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { prepareSliceImplementationContract, migrateSliceContractV2, persistSliceDraft } from './lib/slice-contract-preparation.mjs';
|
|
5
|
+
import { renderSliceContractView, diffSliceContracts } from './lib/slice-contract-views.mjs';
|
|
6
|
+
import { parseSliceYaml, readSliceContract } from './lib/slice-contract.mjs';
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const args=process.argv.slice(2),mode=args.shift(),options={},positional=[];
|
|
10
|
+
for(let i=0;i<args.length;i++) {
|
|
11
|
+
if(args[i]==='--json'){options.json=true;continue;}
|
|
12
|
+
if(args[i].startsWith('--')) {
|
|
13
|
+
const key=args[i].slice(2);if(!['root','input','output','unit','before-root'].includes(key)||!args[i+1])throw new Error(`未知或缺值参数 ${args[i]}`);
|
|
14
|
+
options[key]=args[++i];
|
|
15
|
+
}else positional.push(args[i]);
|
|
16
|
+
}
|
|
17
|
+
const root=path.resolve(options.root||process.cwd());
|
|
18
|
+
let result;
|
|
19
|
+
if(mode==='prepare'||mode==='migrate') {
|
|
20
|
+
const input=options.input?parseSliceYaml(fs.readFileSync(path.resolve(root,options.input))):{};
|
|
21
|
+
if(mode==='prepare')result=prepareSliceImplementationContract({...input,root,ticket_ref:positional[0]});
|
|
22
|
+
else result=migrateSliceContractV2(readSliceContract(positional[0],{root}).raw,{...input,root});
|
|
23
|
+
if(options.output)persistSliceDraft(result,options.output,{root});
|
|
24
|
+
if(result.report.blockers.length)process.exitCode=1;
|
|
25
|
+
}else if(mode==='view')result=renderSliceContractView(positional[0],{root,unit_id:options.unit});
|
|
26
|
+
else if(mode==='diff')result=diffSliceContracts(positional[0],positional[1],{root,beforeRoot:options['before-root']?path.resolve(options['before-root']):root});
|
|
27
|
+
else if(mode==='verify')result={read_only:true,approval_validity:'not-checked',...readSliceContract(positional[0],{root}).binding};
|
|
28
|
+
else throw new Error('用法: scripts/slice-contract prepare <ticket> --input <补充.yaml> --output <新合同.yaml> | migrate <v2.yaml> --input <补充.yaml> --output <新合同.yaml> | view <合同.yaml> [--unit <id>] | diff <旧合同> <新合同> | verify <合同> [--root <项目根>]');
|
|
29
|
+
process.stdout.write(options.json||!result.markdown?`${JSON.stringify(result,null,2)}\n`:`${result.markdown}\n`);
|
|
30
|
+
}catch(error){process.stderr.write(`${error.message}\n`);process.exitCode=1;}
|