create-yss-spec 3.4.6 → 3.4.8
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/package.json +1 -1
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/.yss-skills-manifest.json +1 -8
- package/template/.agents/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.agents/skills/formily-mode-slot-detail/SKILL.md +3 -4
- package/template/.agents/skills/lombok/SKILL.md +1 -1
- package/template/.agents/skills/mapstruct/SKILL.md +3 -1
- package/template/.agents/skills/yedit-table-usage/SKILL.md +2 -0
- package/template/.agents/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.agents/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.agents/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.agents/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.agents/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +6 -4
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.agents/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -88
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.agents/skills/yss-design-system/SKILL.md +2 -2
- package/template/.agents/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.agents/skills/yss-formily/SKILL.md +2 -2
- package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.agents/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/README.md +1 -1
- package/template/.agents/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/package.json +2 -2
- package/template/.agents/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/pnpm-lock.yaml +158 -0
- package/template/.agents/skills/yss-frontend-scaffold-generator/references/data-quality-v1.manifest.json +3 -3
- package/template/.agents/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +5 -0
- package/template/.agents/skills/yss-hook/SKILL.md +3 -2
- package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +6 -1
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -7
- package/template/.agents/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +1 -3
- package/template/.agents/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.agents/skills/yss-skill-source-index-refresh/references/source-map-config.md +3 -2
- package/template/.agents/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +3 -2
- package/template/.agents/skills/yss-technical-design/SKILL.md +1 -1
- package/template/.agents/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.agents/skills/yss-technical-design/tests/run-scenarios.mjs +1 -2
- package/template/.agents/skills/yss-ui/SKILL.md +9 -6
- package/template/.agents/skills/yss-ui/references/component-routing.md +11 -4
- package/template/.agents/skills/yss-ui-business-page-generation/SKILL.md +5 -1
- package/template/.agents/skills/yss-ui-business-page-generation/references/hooks-map.md +3 -2
- package/template/.agents/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.agents/skills/ytable-usage/SKILL.md +14 -7
- package/template/.agents/skills/ytree-usage/SKILL.md +1 -0
- package/template/.codex/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.codex/skills/formily-mode-slot-detail/SKILL.md +3 -4
- package/template/.codex/skills/lombok/SKILL.md +1 -1
- package/template/.codex/skills/mapstruct/SKILL.md +3 -1
- package/template/.codex/skills/yedit-table-usage/SKILL.md +2 -0
- package/template/.codex/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.codex/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.codex/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.codex/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.codex/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +6 -4
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.codex/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -88
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.codex/skills/yss-design-system/SKILL.md +2 -2
- package/template/.codex/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.codex/skills/yss-formily/SKILL.md +2 -2
- package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.codex/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/README.md +1 -1
- package/template/.codex/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/package.json +2 -2
- package/template/.codex/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/pnpm-lock.yaml +158 -0
- package/template/.codex/skills/yss-frontend-scaffold-generator/references/data-quality-v1.manifest.json +3 -3
- package/template/.codex/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +5 -0
- package/template/.codex/skills/yss-hook/SKILL.md +3 -2
- package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +6 -1
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -7
- package/template/.codex/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +1 -3
- package/template/.codex/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.codex/skills/yss-skill-source-index-refresh/references/source-map-config.md +3 -2
- package/template/.codex/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +3 -2
- package/template/.codex/skills/yss-technical-design/SKILL.md +1 -1
- package/template/.codex/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.codex/skills/yss-technical-design/tests/run-scenarios.mjs +1 -2
- package/template/.codex/skills/yss-ui/SKILL.md +9 -6
- package/template/.codex/skills/yss-ui/references/component-routing.md +11 -4
- package/template/.codex/skills/yss-ui-business-page-generation/SKILL.md +5 -1
- package/template/.codex/skills/yss-ui-business-page-generation/references/hooks-map.md +3 -2
- package/template/.codex/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.codex/skills/ytable-usage/SKILL.md +14 -7
- package/template/.codex/skills/ytree-usage/SKILL.md +1 -0
- package/template/.cursor/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.cursor/skills/formily-mode-slot-detail/SKILL.md +3 -4
- package/template/.cursor/skills/lombok/SKILL.md +1 -1
- package/template/.cursor/skills/mapstruct/SKILL.md +3 -1
- package/template/.cursor/skills/yedit-table-usage/SKILL.md +2 -0
- package/template/.cursor/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.cursor/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.cursor/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.cursor/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.cursor/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +6 -4
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.cursor/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -88
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.cursor/skills/yss-design-system/SKILL.md +2 -2
- package/template/.cursor/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.cursor/skills/yss-formily/SKILL.md +2 -2
- package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.cursor/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/README.md +1 -1
- package/template/.cursor/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/package.json +2 -2
- package/template/.cursor/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/pnpm-lock.yaml +158 -0
- package/template/.cursor/skills/yss-frontend-scaffold-generator/references/data-quality-v1.manifest.json +3 -3
- package/template/.cursor/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +5 -0
- package/template/.cursor/skills/yss-hook/SKILL.md +3 -2
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +6 -1
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -7
- package/template/.cursor/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +1 -3
- package/template/.cursor/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.cursor/skills/yss-skill-source-index-refresh/references/source-map-config.md +3 -2
- package/template/.cursor/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +3 -2
- package/template/.cursor/skills/yss-technical-design/SKILL.md +1 -1
- package/template/.cursor/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.cursor/skills/yss-technical-design/tests/run-scenarios.mjs +1 -2
- package/template/.cursor/skills/yss-ui/SKILL.md +9 -6
- package/template/.cursor/skills/yss-ui/references/component-routing.md +11 -4
- package/template/.cursor/skills/yss-ui-business-page-generation/SKILL.md +5 -1
- package/template/.cursor/skills/yss-ui-business-page-generation/references/hooks-map.md +3 -2
- package/template/.cursor/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.cursor/skills/ytable-usage/SKILL.md +14 -7
- package/template/.cursor/skills/ytree-usage/SKILL.md +1 -0
- package/template/.pi/skills/code-review/references/yss-review-standards.md +22 -0
- package/template/.pi/skills/formily-mode-slot-detail/SKILL.md +3 -4
- package/template/.pi/skills/lombok/SKILL.md +1 -1
- package/template/.pi/skills/mapstruct/SKILL.md +3 -1
- package/template/.pi/skills/yedit-table-usage/SKILL.md +2 -0
- package/template/.pi/skills/yss-application/references/application-layer-guide.md +7 -0
- package/template/.pi/skills/yss-backend-spec-review/SKILL.md +38 -0
- package/template/.pi/skills/yss-backend-spec-review/references/check-inputs.md +21 -0
- package/template/.pi/skills/yss-backend-spec-review/references/remediation-workflow.md +48 -0
- package/template/.pi/skills/yss-backend-spec-review/tests/routing.test.mjs +19 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +6 -4
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/java/architecture-rules-test.java.template +60 -23
- package/template/.pi/skills/yss-ddd-scaffold-generator/assets/templates/pom/bootstrap-pom.xml.template +6 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/ARCHITECTURE.md +1 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +4 -2
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/engineering-baseline.md +7 -1
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-compatibility.test.mjs +6 -10
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/first-slice-verification.test.mjs +12 -16
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_first_slice_verification.mjs +36 -88
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs +4 -3
- package/template/.pi/skills/yss-design-system/SKILL.md +2 -2
- package/template/.pi/skills/yss-domain/references/domain-layer-guide.md +8 -1
- package/template/.pi/skills/yss-formily/SKILL.md +2 -2
- package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +1 -1
- package/template/.pi/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/README.md +1 -1
- package/template/.pi/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/package.json +2 -2
- package/template/.pi/skills/yss-frontend-scaffold-generator/assets/data-quality-v1/pnpm-lock.yaml +158 -0
- package/template/.pi/skills/yss-frontend-scaffold-generator/references/data-quality-v1.manifest.json +3 -3
- package/template/.pi/skills/yss-frontend-scaffold-generator/scripts/scaffold-generator.test.mjs +5 -0
- package/template/.pi/skills/yss-hook/SKILL.md +3 -2
- package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +6 -1
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/assets/templates/architecture-rules-test.java.template +66 -0
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/generate_scaffold.mjs +4 -2
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/scripts/scaffold-generator.test.mjs +0 -7
- package/template/.pi/skills/yss-mybatis/SKILL.md +4 -0
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +1 -3
- package/template/.pi/skills/yss-repository/references/profiles/target-domain-model.md +10 -2
- package/template/.pi/skills/yss-skill-source-index-refresh/references/source-map-config.md +3 -2
- package/template/.pi/skills/yss-skill-source-index-refresh/scripts/refresh-yss-skill-index.mjs +3 -2
- package/template/.pi/skills/yss-technical-design/SKILL.md +1 -1
- package/template/.pi/skills/yss-technical-design/references/technical-design-contract.md +1 -1
- package/template/.pi/skills/yss-technical-design/tests/run-scenarios.mjs +1 -2
- package/template/.pi/skills/yss-ui/SKILL.md +9 -6
- package/template/.pi/skills/yss-ui/references/component-routing.md +11 -4
- package/template/.pi/skills/yss-ui-business-page-generation/SKILL.md +5 -1
- package/template/.pi/skills/yss-ui-business-page-generation/references/hooks-map.md +3 -2
- package/template/.pi/skills/yss-web-controller/references/web-adapter-layer-guide.md +8 -0
- package/template/.pi/skills/ytable-usage/SKILL.md +14 -7
- package/template/.pi/skills/ytree-usage/SKILL.md +1 -0
- package/template/docs/agents/digital-human-roles.yaml +2 -2
- package/template/docs/agents/skill-migrations.md +23 -10
- package/template/docs/agents/yss-skill-registry.yaml +14 -73
- package/template/docs/engineering/backend-platforms.md +14 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +657 -100
- package/template/docs/process/schemas/slice-implementation-contract-v3.schema.json +41 -0
- package/template/scripts/lib/backend-platform-provenance.mjs +1 -1
- package/template/scripts/lib/backend-platform-verification.mjs +2 -2
- package/template/scripts/lib/backend-platform.mjs +62 -0
- package/template/scripts/lib/backend-review.mjs +102 -0
- package/template/scripts/lib/first-slice-artifacts.mjs +47 -0
- package/template/scripts/lib/first-slice-tests.mjs +24 -0
- package/template/scripts/lib/lifecycle-transition.mjs +6 -0
- package/template/scripts/lib/maintenance-candidate.mjs +160 -0
- package/template/scripts/lib/skill-registry.mjs +5 -1
- package/template/scripts/lib/skill-supply-chain.mjs +1 -1
- package/template/scripts/lib/slice-execution-preflight.mjs +43 -0
- package/template/scripts/slice-contract +7 -3
- package/template/scripts/verify-backend-platform-matrix +8 -2
- package/template/skills-lock.json +42 -157
- package/template/yss-public-skills.json +5 -7
- package/template.manifest.json +0 -1
- package/template.snapshot.json +5 -5
- package/template/.agents/skills/component-selection-imports/SKILL.md +0 -64
- package/template/.agents/skills/page-form-module/SKILL.md +0 -168
- package/template/.agents/skills/page-list-module/SKILL.md +0 -137
- package/template/.agents/skills/page-skeleton/SKILL.md +0 -76
- package/template/.agents/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
- package/template/.agents/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
- package/template/.agents/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
- package/template/.agents/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
- package/template/.agents/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
- package/template/.agents/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
- package/template/.agents/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
- package/template/.agents/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
- package/template/.agents/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
- package/template/.agents/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
- package/template/.agents/skills/vue3-best-practices/SKILL.md +0 -68
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -12
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
- package/template/.agents/skills/yss-mvc-data-analysis-project-initializer/SKILL.md +0 -12
- package/template/.agents/skills/yss-mvc-data-analysis-project-initializer/scripts/generate_project.mjs +0 -18
- package/template/.agents/skills/yss-mvc-data-analysis-project-initializer/scripts/run_scaffold_verification.mjs +0 -9
- package/template/.agents/skills/yss-mvc-design/SKILL.md +0 -12
- package/template/.agents/skills/yss-mvc-design/agents/openai.yaml +0 -4
- package/template/.agents/skills/yss-mvc-design/references/mvc-design.schema.json +0 -315
- package/template/.agents/skills/yss-use-table-height/SKILL.md +0 -130
- package/template/.agents/skills/yss-use-table-height/agents/openai.yaml +0 -5
- package/template/.agents/skills/yss-use-table-height/references/frontend-docs.md +0 -9
- package/template/.agents/skills/yss-use-tree-height/SKILL.md +0 -98
- package/template/.agents/skills/yss-use-tree-height/agents/openai.yaml +0 -5
- package/template/.agents/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
- package/template/.codex/skills/component-selection-imports/SKILL.md +0 -64
- package/template/.codex/skills/page-form-module/SKILL.md +0 -168
- package/template/.codex/skills/page-list-module/SKILL.md +0 -137
- package/template/.codex/skills/page-skeleton/SKILL.md +0 -76
- package/template/.codex/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
- package/template/.codex/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
- package/template/.codex/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
- package/template/.codex/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
- package/template/.codex/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
- package/template/.codex/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
- package/template/.codex/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
- package/template/.codex/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
- package/template/.codex/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
- package/template/.codex/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
- package/template/.codex/skills/vue3-best-practices/SKILL.md +0 -68
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -12
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
- package/template/.codex/skills/yss-mvc-data-analysis-project-initializer/SKILL.md +0 -12
- package/template/.codex/skills/yss-mvc-data-analysis-project-initializer/scripts/generate_project.mjs +0 -18
- package/template/.codex/skills/yss-mvc-data-analysis-project-initializer/scripts/run_scaffold_verification.mjs +0 -9
- package/template/.codex/skills/yss-mvc-design/SKILL.md +0 -12
- package/template/.codex/skills/yss-mvc-design/agents/openai.yaml +0 -4
- package/template/.codex/skills/yss-mvc-design/references/mvc-design.schema.json +0 -315
- package/template/.codex/skills/yss-use-table-height/SKILL.md +0 -130
- package/template/.codex/skills/yss-use-table-height/agents/openai.yaml +0 -5
- package/template/.codex/skills/yss-use-table-height/references/frontend-docs.md +0 -9
- package/template/.codex/skills/yss-use-tree-height/SKILL.md +0 -98
- package/template/.codex/skills/yss-use-tree-height/agents/openai.yaml +0 -5
- package/template/.codex/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
- package/template/.cursor/skills/component-selection-imports/SKILL.md +0 -64
- package/template/.cursor/skills/page-form-module/SKILL.md +0 -168
- package/template/.cursor/skills/page-list-module/SKILL.md +0 -137
- package/template/.cursor/skills/page-skeleton/SKILL.md +0 -76
- package/template/.cursor/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
- package/template/.cursor/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
- package/template/.cursor/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
- package/template/.cursor/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
- package/template/.cursor/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
- package/template/.cursor/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
- package/template/.cursor/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
- package/template/.cursor/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
- package/template/.cursor/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
- package/template/.cursor/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
- package/template/.cursor/skills/vue3-best-practices/SKILL.md +0 -68
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -12
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
- package/template/.cursor/skills/yss-mvc-data-analysis-project-initializer/SKILL.md +0 -12
- package/template/.cursor/skills/yss-mvc-data-analysis-project-initializer/scripts/generate_project.mjs +0 -18
- package/template/.cursor/skills/yss-mvc-data-analysis-project-initializer/scripts/run_scaffold_verification.mjs +0 -9
- package/template/.cursor/skills/yss-mvc-design/SKILL.md +0 -12
- package/template/.cursor/skills/yss-mvc-design/agents/openai.yaml +0 -4
- package/template/.cursor/skills/yss-mvc-design/references/mvc-design.schema.json +0 -315
- package/template/.cursor/skills/yss-use-table-height/SKILL.md +0 -130
- package/template/.cursor/skills/yss-use-table-height/agents/openai.yaml +0 -5
- package/template/.cursor/skills/yss-use-table-height/references/frontend-docs.md +0 -9
- package/template/.cursor/skills/yss-use-tree-height/SKILL.md +0 -98
- package/template/.cursor/skills/yss-use-tree-height/agents/openai.yaml +0 -5
- package/template/.cursor/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
- package/template/.pi/skills/component-selection-imports/SKILL.md +0 -64
- package/template/.pi/skills/page-form-module/SKILL.md +0 -168
- package/template/.pi/skills/page-list-module/SKILL.md +0 -137
- package/template/.pi/skills/page-skeleton/SKILL.md +0 -76
- package/template/.pi/skills/page-skeleton/examples/AdvancedFormPage/constant.ts +0 -34
- package/template/.pi/skills/page-skeleton/examples/AdvancedFormPage/hooks/useRuleConfig.ts +0 -75
- package/template/.pi/skills/page-skeleton/examples/AdvancedFormPage/index.vue +0 -107
- package/template/.pi/skills/page-skeleton/examples/AdvancedFormPage/schemas/ruleConfigSchema.ts +0 -256
- package/template/.pi/skills/page-skeleton/examples/AdvancedFormPage/style.less +0 -30
- package/template/.pi/skills/page-skeleton/examples/StandardListPage/constant.ts +0 -28
- package/template/.pi/skills/page-skeleton/examples/StandardListPage/hooks/useQualityRuleList.ts +0 -58
- package/template/.pi/skills/page-skeleton/examples/StandardListPage/hooks/useRuleActions.ts +0 -65
- package/template/.pi/skills/page-skeleton/examples/StandardListPage/index.vue +0 -91
- package/template/.pi/skills/page-skeleton/examples/StandardListPage/style.less +0 -49
- package/template/.pi/skills/vue3-best-practices/SKILL.md +0 -68
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/SKILL.md +0 -12
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/yss-backend-scaffold-parent/agents/openai.yaml +0 -5
- package/template/.pi/skills/yss-mvc-data-analysis-project-initializer/SKILL.md +0 -12
- package/template/.pi/skills/yss-mvc-data-analysis-project-initializer/scripts/generate_project.mjs +0 -18
- package/template/.pi/skills/yss-mvc-data-analysis-project-initializer/scripts/run_scaffold_verification.mjs +0 -9
- package/template/.pi/skills/yss-mvc-design/SKILL.md +0 -12
- package/template/.pi/skills/yss-mvc-design/agents/openai.yaml +0 -4
- package/template/.pi/skills/yss-mvc-design/references/mvc-design.schema.json +0 -315
- package/template/.pi/skills/yss-use-table-height/SKILL.md +0 -130
- package/template/.pi/skills/yss-use-table-height/agents/openai.yaml +0 -5
- package/template/.pi/skills/yss-use-table-height/references/frontend-docs.md +0 -9
- package/template/.pi/skills/yss-use-tree-height/SKILL.md +0 -98
- package/template/.pi/skills/yss-use-tree-height/agents/openai.yaml +0 -5
- package/template/.pi/skills/yss-use-tree-height/references/frontend-docs.md +0 -9
|
@@ -902,6 +902,47 @@
|
|
|
902
902
|
]
|
|
903
903
|
}
|
|
904
904
|
}
|
|
905
|
+
},
|
|
906
|
+
"first_slice": {
|
|
907
|
+
"type": "object",
|
|
908
|
+
"additionalProperties": false,
|
|
909
|
+
"required": [
|
|
910
|
+
"artifacts"
|
|
911
|
+
],
|
|
912
|
+
"properties": {
|
|
913
|
+
"artifacts": {
|
|
914
|
+
"type": "array",
|
|
915
|
+
"minItems": 1,
|
|
916
|
+
"items": {
|
|
917
|
+
"type": "object",
|
|
918
|
+
"additionalProperties": false,
|
|
919
|
+
"required": [
|
|
920
|
+
"role",
|
|
921
|
+
"path",
|
|
922
|
+
"type"
|
|
923
|
+
],
|
|
924
|
+
"properties": {
|
|
925
|
+
"role": {
|
|
926
|
+
"enum": [
|
|
927
|
+
"domain",
|
|
928
|
+
"application",
|
|
929
|
+
"infrastructure",
|
|
930
|
+
"web",
|
|
931
|
+
"test"
|
|
932
|
+
]
|
|
933
|
+
},
|
|
934
|
+
"path": {
|
|
935
|
+
"type": "string",
|
|
936
|
+
"minLength": 1
|
|
937
|
+
},
|
|
938
|
+
"type": {
|
|
939
|
+
"type": "string",
|
|
940
|
+
"minLength": 1
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
905
946
|
}
|
|
906
947
|
},
|
|
907
948
|
"required": [
|
|
@@ -21,7 +21,7 @@ export function platformSourceFingerprint(family, root = ROOT) {
|
|
|
21
21
|
const skill = family === 'domain-driven' ? 'yss-ddd-scaffold-generator' : family === 'layered-mvc' ? 'yss-layered-mvc-scaffold-generator' : null;
|
|
22
22
|
if (!skill) fail('unknown architecture');
|
|
23
23
|
const base = `.agents/skills/${skill}`;
|
|
24
|
-
const generator = fileMap(root, [`${base}/scripts/generate_scaffold.mjs`, ...(family === 'domain-driven' ? [`${base}/assets`] : ['.agents/skills/yss-ddd-scaffold-generator/assets/wrapper']), 'scripts/lib/backend-platform.mjs', 'scripts/lib/scaffold-local-database.mjs']);
|
|
24
|
+
const generator = fileMap(root, [`${base}/scripts/generate_scaffold.mjs`, ...(family === 'domain-driven' ? [`${base}/assets`] : [`${base}/assets`, '.agents/skills/yss-ddd-scaffold-generator/assets/wrapper']), 'scripts/lib/backend-platform.mjs', 'scripts/lib/scaffold-local-database.mjs']);
|
|
25
25
|
const verifier = fileMap(root, ['scripts/lib/backend-platform-provenance.mjs', 'scripts/lib/backend-platform-verification.mjs', 'scripts/lib/command-runner.mjs', 'scripts/vendor/xml.mjs', '.agents/skills/yss-ddd-scaffold-generator/scripts/run_scaffold_verification.mjs']);
|
|
26
26
|
return { schema_version: 1, generator_digest: digest(JSON.stringify(generator)), verifier_digest: digest(JSON.stringify(verifier)) };
|
|
27
27
|
}
|
|
@@ -21,9 +21,9 @@ export async function platformEvidenceArtifacts(evidenceDir) {
|
|
|
21
21
|
export function checkPlatformTests(source, capabilities = []) {
|
|
22
22
|
const suite = parseXmlDocument(source).testsuite;
|
|
23
23
|
const cases = array(suite?.testcase);
|
|
24
|
-
const required = ['httpJsonRoundTrip', 'httpValidationRejectsBlank', 'validationAndJsonAutoConfiguration', 'mybatisCanMapVerificationQuery', ...(capabilities.includes('feign-client') ? ['feignCanDecodeJson'] : [])];
|
|
24
|
+
const required = ['httpJsonRoundTrip', 'httpValidationRejectsBlank', 'validationAndJsonAutoConfiguration', 'mybatisCanMapVerificationQuery', 'mappingPersistenceAndPaginationRoundTrip', 'transactionRollsBackOnUseCaseFailure', ...(capabilities.includes('feign-client') ? ['feignCanDecodeJson'] : [])];
|
|
25
25
|
if (!suite || required.some(name => !cases.some(item => item['@_name'] === name && !['failure', 'error', 'skipped'].some(key => Object.hasOwn(item, key))))) fail('integration tests missing, failed or skipped');
|
|
26
|
-
return { status: 'passed', test_count: cases.length };
|
|
26
|
+
return { status: 'passed', test_count: cases.length, scope: 'test-scope mapping, MyBatis RowBounds, TransactionTemplate, HTTP status and JSON; not component error envelope, pagination interceptor, use-case proxy or production dialect certification' };
|
|
27
27
|
}
|
|
28
28
|
export async function verifyPlatformTests(projectRoot, evidenceDir, manifest, startedAt) {
|
|
29
29
|
try {
|
|
@@ -367,6 +367,13 @@ class PlatformIntegrationTest {
|
|
|
367
367
|
try {
|
|
368
368
|
try (java.io.OutputStream stream = connection.getOutputStream()) { stream.write(mapper.writeValueAsBytes(request)); }
|
|
369
369
|
assertEquals(expectedStatus, connection.getResponseCode());
|
|
370
|
+
if (expectedStatus == 400) {
|
|
371
|
+
try (java.io.InputStream stream = connection.getErrorStream()) {
|
|
372
|
+
assertNotNull(stream);
|
|
373
|
+
java.util.Map<?, ?> error = mapper.readValue(stream, java.util.Map.class);
|
|
374
|
+
assertFalse(error.isEmpty()); // Exact YSS error envelope requires the component capability contract.
|
|
375
|
+
}
|
|
376
|
+
}
|
|
370
377
|
if (expectedStatus == 200) {
|
|
371
378
|
assertTrue(connection.getContentType().startsWith("application/json"));
|
|
372
379
|
try (java.io.InputStream stream = connection.getInputStream()) { assertEquals(value, mapper.readValue(stream, ProbePayload.class).getValue()); }
|
|
@@ -375,6 +382,54 @@ class PlatformIntegrationTest {
|
|
|
375
382
|
}
|
|
376
383
|
@Autowired ${mapper.split(".").at(-1)} mapper;
|
|
377
384
|
@Autowired SqlSessionFactory sqlSessionFactory;
|
|
385
|
+
@Autowired org.springframework.jdbc.core.JdbcTemplate jdbc;
|
|
386
|
+
@Autowired org.springframework.transaction.PlatformTransactionManager transactions;
|
|
387
|
+
@Autowired org.mybatis.spring.SqlSessionTemplate sessions;
|
|
388
|
+
@Autowired PlatformProbeMapping probeMapping;
|
|
389
|
+
|
|
390
|
+
@lombok.Getter
|
|
391
|
+
@lombok.Setter
|
|
392
|
+
public static class ProbeRow {
|
|
393
|
+
private Long id;
|
|
394
|
+
private String value;
|
|
395
|
+
}
|
|
396
|
+
interface RoundTripMapper {
|
|
397
|
+
@org.apache.ibatis.annotations.Insert("INSERT INTO scaffold_roundtrip(id, probe_value) VALUES(#{id}, #{value})")
|
|
398
|
+
int insert(ProbeRow row);
|
|
399
|
+
@org.apache.ibatis.annotations.Results(@org.apache.ibatis.annotations.Result(column = "probe_value", property = "value"))
|
|
400
|
+
@Select("SELECT id, probe_value FROM scaffold_roundtrip ORDER BY id")
|
|
401
|
+
java.util.List<ProbeRow> page(org.apache.ibatis.session.RowBounds bounds);
|
|
402
|
+
@Select("SELECT COUNT(*) FROM scaffold_roundtrip") int count();
|
|
403
|
+
}
|
|
404
|
+
private RoundTripMapper roundTripMapper() {
|
|
405
|
+
jdbc.execute("CREATE TABLE IF NOT EXISTS scaffold_roundtrip(id BIGINT PRIMARY KEY, probe_value VARCHAR(100) NOT NULL)");
|
|
406
|
+
jdbc.update("DELETE FROM scaffold_roundtrip");
|
|
407
|
+
if (!sqlSessionFactory.getConfiguration().hasMapper(RoundTripMapper.class)) sqlSessionFactory.getConfiguration().addMapper(RoundTripMapper.class);
|
|
408
|
+
return sessions.getMapper(RoundTripMapper.class);
|
|
409
|
+
}
|
|
410
|
+
@Test void mappingPersistenceAndPaginationRoundTrip() {
|
|
411
|
+
RoundTripMapper persistence = roundTripMapper();
|
|
412
|
+
for (long id = 1; id <= 3; id++) {
|
|
413
|
+
ProbeRow original = new ProbeRow(); original.setId(id); original.setValue("probe-" + id);
|
|
414
|
+
ProbeRow copy = probeMapping.copy(original);
|
|
415
|
+
assertNotSame(original, copy); assertEquals(original.getId(), copy.getId());
|
|
416
|
+
persistence.insert(copy);
|
|
417
|
+
}
|
|
418
|
+
assertEquals(3, persistence.count());
|
|
419
|
+
java.util.List<ProbeRow> page = persistence.page(new org.apache.ibatis.session.RowBounds(1, 1));
|
|
420
|
+
assertEquals(1, page.size()); assertEquals(Long.valueOf(2), page.get(0).getId());
|
|
421
|
+
assertEquals("probe-2", page.get(0).getValue());
|
|
422
|
+
assertNull(probeMapping.copy(null));
|
|
423
|
+
}
|
|
424
|
+
@Test void transactionRollsBackOnUseCaseFailure() {
|
|
425
|
+
final RoundTripMapper persistence = roundTripMapper();
|
|
426
|
+
org.springframework.transaction.support.TransactionTemplate transaction = new org.springframework.transaction.support.TransactionTemplate(transactions);
|
|
427
|
+
assertThrows(IllegalStateException.class, () -> transaction.execute(status -> {
|
|
428
|
+
ProbeRow row = new ProbeRow(); row.setId(9L); row.setValue("must-rollback"); persistence.insert(row);
|
|
429
|
+
throw new IllegalStateException("synthetic use-case failure");
|
|
430
|
+
}));
|
|
431
|
+
assertEquals(0, persistence.count());
|
|
432
|
+
}
|
|
378
433
|
static final class ValidationProbe { @NotBlank String value = ""; }
|
|
379
434
|
@Test void validationAndJsonAutoConfiguration() throws Exception {
|
|
380
435
|
assertFalse(validator.validate(new ValidationProbe()).isEmpty());
|
|
@@ -398,5 +453,12 @@ ${capabilities.includes("feign-client") ? ` @Autowired org.springframework.cl
|
|
|
398
453
|
assertTrue(decoder.decode(response, java.util.Map.class) instanceof java.util.Map);
|
|
399
454
|
}
|
|
400
455
|
` : ""}}
|
|
456
|
+
|
|
457
|
+
@org.mapstruct.Mapper(componentModel = "spring", unmappedTargetPolicy = org.mapstruct.ReportingPolicy.ERROR)
|
|
458
|
+
interface PlatformProbeMapping {
|
|
459
|
+
@org.mapstruct.Mapping(target = "id", source = "id")
|
|
460
|
+
@org.mapstruct.Mapping(target = "value", source = "value")
|
|
461
|
+
PlatformIntegrationTest.ProbeRow copy(PlatformIntegrationTest.ProbeRow row);
|
|
462
|
+
}
|
|
401
463
|
`;
|
|
402
464
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { parseSliceYaml, selectSliceWorkUnit } from './slice-contract.mjs';
|
|
6
|
+
import { inspectSliceContract } from './slice-execution-preflight.mjs';
|
|
7
|
+
import { inspectMaintenanceCandidate } from './maintenance-candidate.mjs';
|
|
8
|
+
|
|
9
|
+
const ensure = (condition, message) => { if (!condition) throw new Error(`backend-review: ${message}`); };
|
|
10
|
+
const text = value => typeof value === 'string' && value.trim();
|
|
11
|
+
const digest = value => createHash('sha256').update(value).digest('hex');
|
|
12
|
+
function git(root, args) {
|
|
13
|
+
const result = spawnSync('git', args, { cwd: root, maxBuffer: 64 * 1024 * 1024 });
|
|
14
|
+
ensure(result.status === 0, `git ${args[0]} failed`); return result.stdout;
|
|
15
|
+
}
|
|
16
|
+
function file(root, ref) {
|
|
17
|
+
ensure(text(ref) && !path.isAbsolute(ref), 'relative evidence reference required');
|
|
18
|
+
const resolved = fs.realpathSync(path.resolve(root, ref));
|
|
19
|
+
ensure(resolved.startsWith(`${fs.realpathSync(root)}${path.sep}`), 'evidence path escape');
|
|
20
|
+
return resolved;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function backendReviewSkills(contract, actualSkills = []) {
|
|
24
|
+
return [...new Set([...(contract.resolution?.required_skills || []), ...(contract.common?.required_skills || []),
|
|
25
|
+
...(contract.backend?.required_skills || []), ...actualSkills, 'alibaba-java-code-style', 'yss-repository', 'yss-mybatis'])].sort();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Validate existing code-review result and constraint_results; this is not an approval authority. */
|
|
29
|
+
export function validateBackendReview(state, { root = process.cwd() } = {}) {
|
|
30
|
+
const input = state?.review_input;
|
|
31
|
+
ensure(input?.slice_contract_ref, 'review input must bind Slice contract');
|
|
32
|
+
const loaded = inspectSliceContract(input.slice_contract_ref, { root, approval_ref: input.approval_ref, work_unit_id: input.work_unit_id });
|
|
33
|
+
ensure(loaded.report.execution_allowed, `approved current contract required: ${loaded.report.blockers.join('; ')}`);
|
|
34
|
+
const contract = selectSliceWorkUnit(loaded.contract, input.work_unit_id);
|
|
35
|
+
if (contract.backend?.status !== 'required') return { status: 'not-applicable', reason: 'contract has no backend impact' };
|
|
36
|
+
ensure(Array.isArray(input.actual_skill_impacts), 'actual_skill_impacts must be explicit (may be empty)');
|
|
37
|
+
const result = parseSliceYaml(fs.readFileSync(file(root, state.review_result_ref), 'utf8'));
|
|
38
|
+
ensure(result.skill === 'code-review' && result.result === 'completed', 'only completed code-review can proceed');
|
|
39
|
+
ensure(result.contract_digest === loaded.binding.digest, 'review contract digest stale');
|
|
40
|
+
for (const identity of ['reviewer','implementer']) {
|
|
41
|
+
const actor = result[identity];
|
|
42
|
+
for (const field of ['actor_id','runtime_id','instance_id']) ensure(text(actor?.[field]), `${identity}.${field} required`);
|
|
43
|
+
}
|
|
44
|
+
ensure(result.reviewer.actor_id !== result.implementer.actor_id && result.reviewer.instance_id !== result.implementer.instance_id, 'independent Reviewer required');
|
|
45
|
+
ensure(result.implementer.actor_id === input.implementation_actor_id && result.implementer.instance_id === input.implementation_instance_id, 'implementation identity mismatch');
|
|
46
|
+
ensure(result.candidate_digest === input.candidate_digest, 'candidate digest mismatch');
|
|
47
|
+
const projectRoot = fs.realpathSync(path.resolve(root, input.project_root || '.'));
|
|
48
|
+
ensure(contract.common.project_roots.some(ref => fs.realpathSync(path.resolve(root, ref)) === projectRoot), 'candidate project is outside approved contract');
|
|
49
|
+
// Reuse the packed worktree candidate protocol. Its exclusions are restricted to review evidence.
|
|
50
|
+
if (input.review_mode === 'worktree') {
|
|
51
|
+
const { manifest, trackedDiff, entries } = inspectMaintenanceCandidate({ manifestPath: file(projectRoot, input.candidate_snapshot_ref) });
|
|
52
|
+
ensure(manifest.candidate_digest === input.candidate_digest, 'snapshot digest mismatch');
|
|
53
|
+
ensure(trackedDiff.equals(git(projectRoot, ['diff','--no-ext-diff','--binary','--full-index',manifest.merge_base])), 'tracked candidate stale');
|
|
54
|
+
const excluded = manifest.excluded_paths || [];
|
|
55
|
+
ensure(excluded.every(p => p.startsWith('.template-source/evidence/maintenance/') && !p.split('/').includes('..')), 'candidate exclusions must be evidence only');
|
|
56
|
+
const actual = git(projectRoot, ['ls-files','-z','--others','--exclude-standard']).toString().split('\0').filter(Boolean)
|
|
57
|
+
.filter(p => !excluded.some(ex => p === ex || p.startsWith(`${ex}/`))).sort();
|
|
58
|
+
ensure(JSON.stringify(actual) === JSON.stringify(entries.map(e => e.path).sort()), 'untracked candidate inventory stale');
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
const info = fs.lstatSync(path.resolve(projectRoot, entry.path));
|
|
61
|
+
ensure(info.mode === entry.mode && (entry.kind === 'symlink' ? info.isSymbolicLink() : info.isFile()), `untracked candidate mode/kind stale: ${entry.path}`);
|
|
62
|
+
const current = entry.kind === 'symlink' ? Buffer.from(fs.readlinkSync(path.resolve(projectRoot, entry.path))) : fs.readFileSync(file(projectRoot, entry.path));
|
|
63
|
+
ensure(current.equals(entry.content), `untracked candidate stale: ${entry.path}`);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
ensure(input.review_mode === 'committed', 'unsupported candidate mode');
|
|
67
|
+
const tree = git(projectRoot, ['rev-parse',`${input.implementation_candidate_ref}^{tree}`]).toString().trim();
|
|
68
|
+
ensure(tree === input.candidate_digest, 'committed candidate mismatch');
|
|
69
|
+
ensure(git(projectRoot, ['rev-parse','HEAD^{tree}']).toString().trim() === tree && !git(projectRoot, ['status','--porcelain']).length, 'committed candidate is not current clean checkout');
|
|
70
|
+
}
|
|
71
|
+
const layerSkills = { domain: 'yss-domain', application: 'yss-application', infrastructure: 'yss-repository', web: 'yss-web-controller' };
|
|
72
|
+
const actualSkills = [...input.actual_skill_impacts, ...(contract.backend.affected_layers || []).map(layer => layerSkills[layer]).filter(Boolean)];
|
|
73
|
+
const required = backendReviewSkills(contract, actualSkills);
|
|
74
|
+
const applicable = new Set([...(contract.resolution?.required_skills || []), ...(contract.common?.required_skills || []), ...(contract.backend?.required_skills || []), ...actualSkills, "alibaba-java-code-style"]);
|
|
75
|
+
ensure(Array.isArray(result.constraint_results), 'constraint_results required');
|
|
76
|
+
for (const skill of required) {
|
|
77
|
+
const rows = result.constraint_results.filter(row => row.skill === skill && row.axis === 'Standards');
|
|
78
|
+
ensure(rows.length, `skill not reviewed: ${skill}`);
|
|
79
|
+
for (const row of rows) {
|
|
80
|
+
ensure(text(row.constraint) && text(row.rule_ref) && text(row.code_ref) && text(row.evidence_ref), `${skill}: rule/code/evidence required`);
|
|
81
|
+
ensure(['passed','not-applicable'].includes(row.status), `${skill}: unresolved violation`);
|
|
82
|
+
ensure(row.status !== 'not-applicable' || (text(row.reason) && !applicable.has(skill)), `${skill}: applicable skill cannot be waived`);
|
|
83
|
+
ensure(row.rule_ref.startsWith(`.agents/skills/${skill}/`), `${skill}: rule must cite its canonical skill`);
|
|
84
|
+
const rule = fs.readFileSync(file(root, row.rule_ref.split('#')[0]));
|
|
85
|
+
ensure(digest(rule) === row.rule_digest, `${skill}: rule evidence stale`);
|
|
86
|
+
ensure(fs.existsSync(file(projectRoot, row.code_ref.split(':')[0])), `${skill}: missing code location`);
|
|
87
|
+
ensure(digest(fs.readFileSync(file(root, row.evidence_ref))) === row.evidence_digest, `${skill}: verification evidence stale`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
ensure(Array.isArray(result.verification_results) && result.verification_results.length > 0, 'machine verification required');
|
|
91
|
+
for (const check of result.verification_results) {
|
|
92
|
+
ensure(text(check.command) && Number.isFinite(Date.parse(check.executed_at)) && check.exit_code === 0 && check.candidate_digest === result.candidate_digest, 'machine check failed, absent or stale');
|
|
93
|
+
ensure(digest(fs.readFileSync(file(root, check.evidence_ref))) === check.evidence_digest, 'machine evidence stale');
|
|
94
|
+
}
|
|
95
|
+
ensure(result.axes?.Standards === 'passed' && result.axes?.Spec === 'passed', 'Standards and Spec must pass separately');
|
|
96
|
+
ensure(Array.isArray(result.findings), 'findings required');
|
|
97
|
+
for (const finding of result.findings) {
|
|
98
|
+
ensure(!['violation','drift','new_impacts','missing_evidence'].includes(finding.disposition) || finding.status === 'resolved', 'blocking finding remains open');
|
|
99
|
+
if (finding.disposition === 'suggestion' && finding.status !== 'resolved') ensure(text(finding.follow_up_ref), 'suggestion needs backlog reference');
|
|
100
|
+
}
|
|
101
|
+
return { status: 'passed', candidate_digest: input.candidate_digest, reviewed_skills: required };
|
|
102
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/** Declarations bind responsibilities to compiled types, not naming conventions. */
|
|
5
|
+
export function inspectFirstSliceArtifacts(contract, projectRoot) {
|
|
6
|
+
const declaration = contract.backend?.first_slice;
|
|
7
|
+
const failures = [], properties = { 'yss.firstSlice': 'true' };
|
|
8
|
+
if (!declaration || !Array.isArray(declaration.artifacts) || !declaration.artifacts.length) {
|
|
9
|
+
return { failures: ['backend.first_slice.artifacts-required'], properties };
|
|
10
|
+
}
|
|
11
|
+
const roles = new Map(), types = [], seen = new Set();
|
|
12
|
+
const root = fs.realpathSync(projectRoot);
|
|
13
|
+
for (const item of declaration.artifacts) {
|
|
14
|
+
const label = item?.type || item?.path || 'unknown';
|
|
15
|
+
try {
|
|
16
|
+
if (!['domain','application','infrastructure','web','test'].includes(item.role)) throw Error('invalid-role');
|
|
17
|
+
if (!/^[a-zA-Z_$][\w$]*(\.[a-zA-Z_$][\w$]*)+$/.test(item.type || '')) throw Error('qualified-type-required');
|
|
18
|
+
if (seen.has(item.type)) throw Error('duplicate-type');
|
|
19
|
+
seen.add(item.type);
|
|
20
|
+
if (!item.path || path.isAbsolute(item.path) || item.path.split(/[\\/]/).includes('..')) throw Error('relative-path-required');
|
|
21
|
+
const file = fs.realpathSync(path.resolve(root, item.path));
|
|
22
|
+
if (!file.startsWith(`${root}${path.sep}`)) throw Error('path-escape');
|
|
23
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
24
|
+
const pkg = source.match(/^\s*package\s+([\w.]+)\s*;/m)?.[1];
|
|
25
|
+
const name = item.type.slice(item.type.lastIndexOf('.') + 1);
|
|
26
|
+
if (`${pkg}.${name}` !== item.type || !new RegExp(`\\b(class|interface|enum|record)\\s+${name}\\b`).test(source)) throw Error('declared-type-mismatch');
|
|
27
|
+
if (item.role === 'test') {
|
|
28
|
+
if (!item.path.includes('/src/test/java/')) throw Error('test-source-required');
|
|
29
|
+
} else {
|
|
30
|
+
if (!item.path.includes('/src/main/java/')) throw Error('main-source-required');
|
|
31
|
+
types.push(item.type);
|
|
32
|
+
const packages = roles.get(item.role) || new Set(); packages.add(pkg); roles.set(item.role, packages);
|
|
33
|
+
}
|
|
34
|
+
} catch (error) { failures.push(`${label}:${error.message}`); }
|
|
35
|
+
}
|
|
36
|
+
const aliases = { service: 'application', core: 'domain', repository: 'infrastructure', server: 'web' };
|
|
37
|
+
for (const layer of contract.backend.affected_layers || []) {
|
|
38
|
+
const role = aliases[layer] || layer;
|
|
39
|
+
if (['domain','application','infrastructure','web'].includes(role) && !roles.has(role)) failures.push(`affected-role-empty:${role}`);
|
|
40
|
+
}
|
|
41
|
+
if (!declaration.artifacts.some(item => item.role === 'test')) failures.push('behavior-test-binding-required');
|
|
42
|
+
if ((contract.backend.affected_layers || []).some(layer => ['web','server'].includes(layer))) properties['yss.arch.requireController'] = 'true';
|
|
43
|
+
if (!types.length) failures.push('business-type-required');
|
|
44
|
+
properties['yss.arch.requiredTypes'] = types.join(',');
|
|
45
|
+
for (const [role, packages] of roles) properties[`yss.arch.${role}`] = [...packages].join(',');
|
|
46
|
+
return { failures, properties };
|
|
47
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { parseXmlDocument } from '../vendor/xml.mjs';
|
|
5
|
+
const array = value => value === undefined ? [] : Array.isArray(value) ? value : [value];
|
|
6
|
+
|
|
7
|
+
export function verifyFirstSliceTests(contract, projectRoot, startedAt, architectureType) {
|
|
8
|
+
const declarations = contract.backend.first_slice.artifacts.filter(item => item.role === 'test');
|
|
9
|
+
const required = [...declarations.map(item => ({ type: item.type, module: item.path.split('/src/test/java/')[0] })), architectureType];
|
|
10
|
+
const evidence = [], failures = [];
|
|
11
|
+
for (const item of required) {
|
|
12
|
+
try {
|
|
13
|
+
const candidates = ['surefire-reports','failsafe-reports'].map(folder => path.join(projectRoot,item.module,'target',folder,`TEST-${item.type}.xml`));
|
|
14
|
+
const ref = candidates.find(file => fs.existsSync(file) && fs.statSync(file).mtimeMs >= startedAt);
|
|
15
|
+
if (!ref) throw Error('fresh execution report missing');
|
|
16
|
+
const bytes = fs.readFileSync(ref), suite = parseXmlDocument(bytes.toString()).testsuite;
|
|
17
|
+
const cases = array(suite?.testcase);
|
|
18
|
+
if (!cases.length || cases.some(c => ['failure','error','skipped'].some(key => Object.hasOwn(c,key)))) throw Error('test not executed successfully');
|
|
19
|
+
if (cases.some(c => c['@_classname'] !== item.type)) throw Error('test class does not match contract');
|
|
20
|
+
evidence.push({type:item.type,ref,sha256:createHash('sha256').update(bytes).digest('hex'),test_count:cases.length});
|
|
21
|
+
} catch(error) { failures.push(`${item.type}:${error.message}`); }
|
|
22
|
+
}
|
|
23
|
+
return { status: failures.length ? 'failed' : 'passed', evidence, failures };
|
|
24
|
+
}
|
|
@@ -9,6 +9,7 @@ import { validatePlanSpecEntry } from "./plan-spec-entry.mjs";
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { validateApiContractDecision } from "./api-contract-decision.mjs";
|
|
11
11
|
import { loadApprovalRecord, validateApprovalRecord } from "./approval-record.mjs";
|
|
12
|
+
import { validateBackendReview } from "./backend-review.mjs";
|
|
12
13
|
import { ROOT } from "./lifecycle-registry.mjs";
|
|
13
14
|
|
|
14
15
|
const IMPLEMENTATION_WORK_UNIT = "work-unit.slice-implementation";
|
|
@@ -248,6 +249,11 @@ function validateTicketReference(ref, trackerKind) {
|
|
|
248
249
|
* by `validateWorkflowExecutionResult` before this function is called.
|
|
249
250
|
*/
|
|
250
251
|
export function validateNextRoute(currentWorkUnit, nextRoute, decisionState, options = {}) {
|
|
252
|
+
if (currentWorkUnit === "work-unit.code-review" && nextRoute === "work-unit.release-and-retrospective") {
|
|
253
|
+
try { validateBackendReview(decisionState, { root: options.root || ROOT }); }
|
|
254
|
+
catch (error) { return blockedResult(["backend-review-incomplete"], [error.message]); }
|
|
255
|
+
}
|
|
256
|
+
|
|
251
257
|
|
|
252
258
|
if (["work-unit.technical-analysis", TICKET_DECOMPOSITION_WORK_UNIT, IMPLEMENTATION_WORK_UNIT, "work-unit.frontend-implementation-verification"].includes(nextRoute)) {
|
|
253
259
|
try { enforceFrontendDelivery(decisionState, { root: options.root, phase: nextRoute === IMPLEMENTATION_WORK_UNIT ? "implementation" : "inputs" }); }
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { parseDocument, stringify } from "../vendor/yaml.mjs";
|
|
6
|
+
|
|
7
|
+
const MAGIC = Buffer.from("YSS-WORKTREE-CANDIDATE-V1\0", "ascii");
|
|
8
|
+
const MAX_BUFFER = 512 * 1024 * 1024;
|
|
9
|
+
const MAINTENANCE_EVIDENCE_ROOT = ".template-source/evidence/maintenance";
|
|
10
|
+
const CANDIDATE_FILES = ["candidate-manifest.yaml", "candidate.bin", "tracked.diff"];
|
|
11
|
+
|
|
12
|
+
function fail(message) { throw new TypeError(message); }
|
|
13
|
+
function ensure(condition, message) { if (!condition) fail(message); }
|
|
14
|
+
function git(args, root, encoding = "utf8") {
|
|
15
|
+
const result = spawnSync("git", args, { cwd: root, encoding, maxBuffer: MAX_BUFFER });
|
|
16
|
+
if (result.status !== 0) fail((typeof result.stderr === "string" ? result.stderr : result.stderr?.toString()).trim() || `git ${args.join(" ")} 执行失败`);
|
|
17
|
+
return result.stdout;
|
|
18
|
+
}
|
|
19
|
+
function uint64(value) { const buffer = Buffer.alloc(8); buffer.writeBigUInt64BE(BigInt(value)); return buffer; }
|
|
20
|
+
function uint32(value) { const buffer = Buffer.alloc(4); buffer.writeUInt32BE(value >>> 0); return buffer; }
|
|
21
|
+
function splitNull(buffer) {
|
|
22
|
+
const result = [];
|
|
23
|
+
let start = 0;
|
|
24
|
+
for (let index = 0; index < buffer.length; index += 1) if (buffer[index] === 0) { if (index > start) result.push(buffer.subarray(start, index)); start = index + 1; }
|
|
25
|
+
if (start < buffer.length) result.push(buffer.subarray(start));
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
function normalizeRelative(value, label) {
|
|
29
|
+
ensure(typeof value === "string" && value.trim(), `${label} 不能为空`);
|
|
30
|
+
const normalized = value.replaceAll("\\", "/").replace(/^\.\//, "").replace(/\/$/, "");
|
|
31
|
+
ensure(normalized && !path.posix.isAbsolute(normalized) && path.posix.normalize(normalized) === normalized && normalized !== ".." && !normalized.startsWith("../"), `${label} 必须是规范的仓库内相对路径`);
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
function isMaintenanceEvidencePath(value) {
|
|
35
|
+
return value === MAINTENANCE_EVIDENCE_ROOT || value.startsWith(`${MAINTENANCE_EVIDENCE_ROOT}/`);
|
|
36
|
+
}
|
|
37
|
+
function readLength(buffer, offset, bytes, label) {
|
|
38
|
+
ensure(offset + bytes <= buffer.length, `packed candidate 缺少 ${label}`);
|
|
39
|
+
const value = bytes === 8 ? buffer.readBigUInt64BE(offset) : BigInt(buffer.readUInt32BE(offset));
|
|
40
|
+
ensure(value <= BigInt(Number.MAX_SAFE_INTEGER), `packed candidate ${label} 过大`);
|
|
41
|
+
return { value: Number(value), offset: offset + bytes };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function captureMaintenanceCandidate({ root, outputDir, baseRef = "HEAD", excludePaths = [] }) {
|
|
45
|
+
const repositoryRoot = path.resolve(root);
|
|
46
|
+
const outputPath = path.resolve(repositoryRoot, outputDir);
|
|
47
|
+
const outputRelative = normalizeRelative(path.relative(repositoryRoot, outputPath).replaceAll(path.sep, "/"), "outputDir");
|
|
48
|
+
ensure(isMaintenanceEvidencePath(outputRelative), `outputDir 必须位于 ${MAINTENANCE_EVIDENCE_ROOT}`);
|
|
49
|
+
ensure(!existsSync(outputPath), "outputDir 必须不存在,禁止复用可能含残留文件的候选目录");
|
|
50
|
+
ensure(Array.isArray(excludePaths), "excludePaths 必须是数组");
|
|
51
|
+
const explicitExclusions = excludePaths.map((value) => normalizeRelative(value, "excludePaths"));
|
|
52
|
+
for (const exclusion of explicitExclusions) ensure(isMaintenanceEvidencePath(exclusion), `excludePaths 只允许 ${MAINTENANCE_EVIDENCE_ROOT} 下的维护证据`);
|
|
53
|
+
const exclusions = [...new Set([outputRelative, ...explicitExclusions])];
|
|
54
|
+
const mergeBase = git(["merge-base", baseRef, "HEAD"], repositoryRoot).trim();
|
|
55
|
+
const trackedDiff = git(["diff", "--no-ext-diff", "--binary", "--full-index", mergeBase], repositoryRoot, null);
|
|
56
|
+
const rawPaths = splitNull(git(["ls-files", "-z", "--others", "--exclude-standard"], repositoryRoot, null))
|
|
57
|
+
.filter((rawPath) => {
|
|
58
|
+
const value = rawPath.toString();
|
|
59
|
+
return !exclusions.some((exclusion) => value === exclusion || value.startsWith(`${exclusion}/`));
|
|
60
|
+
})
|
|
61
|
+
.sort(Buffer.compare);
|
|
62
|
+
ensure(trackedDiff.length > 0 || rawPaths.length > 0, "维护候选不能为空");
|
|
63
|
+
|
|
64
|
+
const parts = [MAGIC, Buffer.from([0x54]), uint64(trackedDiff.length), trackedDiff];
|
|
65
|
+
const untrackedFiles = [];
|
|
66
|
+
const untrackedPathBytes = [];
|
|
67
|
+
for (const rawPath of rawPaths) {
|
|
68
|
+
ensure(!rawPath.includes(0) && rawPath.length > 0, "untracked path 无效");
|
|
69
|
+
const relative = rawPath.toString();
|
|
70
|
+
const absolute = path.resolve(repositoryRoot, relative);
|
|
71
|
+
ensure(!path.relative(repositoryRoot, absolute).startsWith(".."), `untracked path 越界: ${relative}`);
|
|
72
|
+
const stat = lstatSync(absolute);
|
|
73
|
+
let kind;
|
|
74
|
+
let content;
|
|
75
|
+
if (stat.isFile()) { kind = 0x52; content = readFileSync(absolute); }
|
|
76
|
+
else if (stat.isSymbolicLink()) { kind = 0x4c; content = readlinkSync(absolute, { encoding: "buffer" }); }
|
|
77
|
+
else fail(`候选包含不支持的 untracked 类型: ${relative}`);
|
|
78
|
+
parts.push(Buffer.from([0x55]), uint64(rawPath.length), rawPath, uint32(stat.mode), Buffer.from([kind]), uint64(content.length), content);
|
|
79
|
+
untrackedFiles.push(relative);
|
|
80
|
+
untrackedPathBytes.push(rawPath.toString("base64"));
|
|
81
|
+
}
|
|
82
|
+
const stream = Buffer.concat(parts);
|
|
83
|
+
const digest = createHash("sha256").update(stream).digest("hex");
|
|
84
|
+
const streamRef = `${outputRelative}/candidate.bin`;
|
|
85
|
+
const diffRef = `${outputRelative}/tracked.diff`;
|
|
86
|
+
const manifestRef = `${outputRelative}/candidate-manifest.yaml`;
|
|
87
|
+
const manifest = {
|
|
88
|
+
schema_version: 1,
|
|
89
|
+
candidate_kind: "yss-worktree-candidate-v1",
|
|
90
|
+
storage: "packed-stream",
|
|
91
|
+
review_mode: "worktree",
|
|
92
|
+
review_base_ref: baseRef,
|
|
93
|
+
merge_base: mergeBase,
|
|
94
|
+
implementation_candidate_ref: "working-tree",
|
|
95
|
+
candidate_snapshot_ref: manifestRef,
|
|
96
|
+
candidate_digest: digest,
|
|
97
|
+
tracked_diff_command: `git diff --no-ext-diff --binary --full-index ${mergeBase}`,
|
|
98
|
+
untracked_inventory_command: "git ls-files -z --others --exclude-standard",
|
|
99
|
+
untracked_diff_command: "packed in candidate.bin",
|
|
100
|
+
untracked_files: untrackedFiles,
|
|
101
|
+
untracked_path_bytes: untrackedPathBytes,
|
|
102
|
+
excluded_paths: exclusions,
|
|
103
|
+
snapshot_stream_ref: streamRef,
|
|
104
|
+
tracked_diff_ref: diffRef,
|
|
105
|
+
commit_list_command: `git log ${baseRef}..HEAD --oneline`
|
|
106
|
+
};
|
|
107
|
+
const outputParent = path.dirname(outputPath);
|
|
108
|
+
mkdirSync(outputParent, { recursive: true });
|
|
109
|
+
const stagingPath = mkdtempSync(path.join(outputParent, ".candidate-staging-"));
|
|
110
|
+
try {
|
|
111
|
+
writeFileSync(path.join(stagingPath, "candidate.bin"), stream);
|
|
112
|
+
writeFileSync(path.join(stagingPath, "tracked.diff"), trackedDiff);
|
|
113
|
+
writeFileSync(path.join(stagingPath, "candidate-manifest.yaml"), stringify(manifest, { lineWidth: 0 }));
|
|
114
|
+
renameSync(stagingPath, outputPath);
|
|
115
|
+
} finally {
|
|
116
|
+
if (existsSync(stagingPath)) rmSync(stagingPath, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
return { candidate_digest: digest, manifest, manifest_ref: manifestRef, files: [manifestRef, streamRef, diffRef] };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function inspectMaintenanceCandidate({ manifestPath }) {
|
|
122
|
+
const absoluteManifest = path.resolve(manifestPath);
|
|
123
|
+
const document = parseDocument(readFileSync(absoluteManifest, "utf8"), { uniqueKeys: true });
|
|
124
|
+
ensure(document.errors.length === 0, document.errors[0]?.message || "candidate manifest 无法解析");
|
|
125
|
+
const manifest = document.toJS({ maxAliasCount: 0 });
|
|
126
|
+
ensure(manifest.storage === "packed-stream", "只支持 packed-stream candidate");
|
|
127
|
+
const candidateDir = path.dirname(absoluteManifest);
|
|
128
|
+
ensure(JSON.stringify(readdirSync(candidateDir).sort()) === JSON.stringify([...CANDIDATE_FILES].sort()), `packed candidate 目录必须恰好包含 ${CANDIDATE_FILES.join("、")}`);
|
|
129
|
+
const root = path.resolve(path.dirname(absoluteManifest), ...Array(manifest.candidate_snapshot_ref.split("/").length - 1).fill(".."));
|
|
130
|
+
ensure(path.resolve(root, manifest.candidate_snapshot_ref) === absoluteManifest, "candidate_snapshot_ref 未绑定当前 manifest");
|
|
131
|
+
const streamPath = path.resolve(root, manifest.snapshot_stream_ref);
|
|
132
|
+
const diffPath = path.resolve(root, manifest.tracked_diff_ref);
|
|
133
|
+
ensure(streamPath === path.join(candidateDir, "candidate.bin") && diffPath === path.join(candidateDir, "tracked.diff"), "packed candidate 引用必须绑定同目录规范文件");
|
|
134
|
+
const stream = readFileSync(streamPath);
|
|
135
|
+
ensure(createHash("sha256").update(stream).digest("hex") === manifest.candidate_digest.replace(/^sha256:/, ""), "candidate_digest 与 packed stream 不一致");
|
|
136
|
+
ensure(stream.subarray(0, MAGIC.length).equals(MAGIC), "packed stream magic 无效");
|
|
137
|
+
let offset = MAGIC.length;
|
|
138
|
+
ensure(stream[offset] === 0x54, "packed stream 缺少 tracked record");
|
|
139
|
+
offset += 1;
|
|
140
|
+
let length = readLength(stream, offset, 8, "tracked length");
|
|
141
|
+
offset = length.offset;
|
|
142
|
+
const trackedDiff = stream.subarray(offset, offset + length.value);
|
|
143
|
+
offset += length.value;
|
|
144
|
+
ensure(readFileSync(diffPath).equals(trackedDiff), "tracked.diff 与 packed stream 不一致");
|
|
145
|
+
const entries = [];
|
|
146
|
+
while (offset < stream.length) {
|
|
147
|
+
ensure(stream[offset] === 0x55, "packed stream record kind 无效");
|
|
148
|
+
offset += 1;
|
|
149
|
+
length = readLength(stream, offset, 8, "path length"); offset = length.offset;
|
|
150
|
+
const rawPath = stream.subarray(offset, offset + length.value); offset += length.value;
|
|
151
|
+
length = readLength(stream, offset, 4, "mode"); offset = length.offset;
|
|
152
|
+
const mode = length.value;
|
|
153
|
+
const kind = stream[offset]; offset += 1;
|
|
154
|
+
length = readLength(stream, offset, 8, "content length"); offset = length.offset;
|
|
155
|
+
const content = stream.subarray(offset, offset + length.value); offset += length.value;
|
|
156
|
+
entries.push({ path: rawPath.toString(), path_bytes: rawPath.toString("base64"), mode, kind: kind === 0x52 ? "regular" : "symlink", content });
|
|
157
|
+
}
|
|
158
|
+
ensure(JSON.stringify(entries.map((entry) => entry.path_bytes)) === JSON.stringify(manifest.untracked_path_bytes), "manifest inventory 与 packed stream 不一致");
|
|
159
|
+
return { manifest, trackedDiff, entries };
|
|
160
|
+
}
|
|
@@ -6,7 +6,11 @@ import { nestedSkillPaths, OBSOLETE, PROJECTION_ROOTS, ROOT, unregisteredNestedS
|
|
|
6
6
|
export const DEFAULT_REGISTRY = path.join(ROOT, "docs/agents/yss-skill-registry.yaml");
|
|
7
7
|
const LOCK_PATH = path.join(ROOT, "skills-lock.json");
|
|
8
8
|
const COMPILER_CONTRACT = path.join(ROOT, ".agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml");
|
|
9
|
-
const LIFECYCLE_CONTRACT =
|
|
9
|
+
const LIFECYCLE_CONTRACT = [
|
|
10
|
+
path.join(ROOT, ".agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml"),
|
|
11
|
+
path.join(ROOT, ".agents/skills/harness-orchestrator/references/orchestration-contract.yaml"),
|
|
12
|
+
].find((candidate) => existsSync(candidate))
|
|
13
|
+
?? path.join(ROOT, ".agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml");
|
|
10
14
|
const BACKEND_PLATFORMS = path.join(ROOT, "docs/engineering/backend-platforms.json");
|
|
11
15
|
const LAYERS = new Set(["core", "specialist", "compatibility", "maintainer-only"]);
|
|
12
16
|
const MATURITIES = new Set(["draft", "verified", "supported", "deprecated"]);
|
|
@@ -11,7 +11,7 @@ const YSS_UI_MANIFEST_PATH = path.join(SOURCE_ROOT, ".yss-skills-manifest.json")
|
|
|
11
11
|
const STRATEGIC_DESIGN_MANIFEST_PATH = path.join(SOURCE_ROOT, ".strategic-design-skills-manifest.json");
|
|
12
12
|
export const PROJECTION_ROOTS = [".codex/skills", ".cursor/skills", ".pi/skills"];
|
|
13
13
|
export const OBSOLETE = new Set(["yss-antd-design", "yss-antdv-next-design", "to-" + "prd", "to-" + "issues", "design-an-interface", "qa", "request-refactor-plan", "ubiquitous-language", "edit-article", "obsidian-vault", "writing-great-skills", "code-review-process", "yss-domain-modeling", "yss-dir", "yss-duckdb", "yss-file", "yss-filerunner", "yss-db2mybatis", "yss-mail", "yss-mapper-dynamic", "yss-quality", "yss-sql-condition", "yss-sql-tpl", "yss-valuation", "yss-variable", "yss-openapi", "web-design-engineer", "web-video-presentation", "wireframe-prototype", "wizard", "git-guardrails-claude-code", "claude-handoff", "batch-grill-me", "product-design-prototype", "research", "ask-matt", "dispatching-parallel-agents", "loop-me", "migrate-to-shoehorn", "prototype-page-acceptance", "scaffold-exercises", "setup-pre-commit", "setup-ts-deep-modules", "teach", "writing-beats", "writing-fragments", "writing-shape", "yss-microapp-commit", "yss-page-module-development", "yss-dictionary", "yss-jdbc", "yss-log", "yss-taskflow", "yss-backend-scaffold-application", "yss-backend-scaffold-domain", "yss-backend-scaffold-infrastructure", "yss-backend-scaffold-web", "yss-router", "yss-source-index"]);
|
|
14
|
-
for (const id of ["yss-components", "yss-mvc-scaffold-generator", "yss-backend-scaffold-adapter", "yss-application-layer-reference", "yss-domain-layer-reference", "yss-infrastructure-layer-reference", "yss-web-layer-reference"]) OBSOLETE.add(id);
|
|
14
|
+
for (const id of ["component-selection-imports", "page-form-module", "page-list-module", "page-skeleton", "vue3-best-practices", "yss-use-table-height", "yss-use-tree-height", "yss-components", "yss-mvc-scaffold-generator", "yss-mvc-design", "yss-mvc-data-analysis-project-initializer", "yss-backend-scaffold-parent", "yss-backend-scaffold-adapter", "yss-application-layer-reference", "yss-domain-layer-reference", "yss-infrastructure-layer-reference", "yss-web-layer-reference"]) OBSOLETE.add(id);
|
|
15
15
|
export function obsoleteCanonicalResidues(names, obsolete = OBSOLETE) {
|
|
16
16
|
return names.filter((name) => obsolete.has(name)).sort();
|
|
17
17
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readSliceContract } from './slice-contract.mjs';
|
|
2
|
+
import { createApprovedExecutionContext } from './approved-execution-context.mjs';
|
|
3
|
+
import { evaluateContractFreshness, loadCompilerContract } from './implementation-contract-compiler.mjs';
|
|
4
|
+
import { loadSkillRegistry } from './skill-registry.mjs';
|
|
5
|
+
|
|
6
|
+
/** Readability, structure and execution approval are different levels of evidence. */
|
|
7
|
+
export function inspectSliceContract(ref, { root = process.cwd(), approval_ref, work_unit_id } = {}) {
|
|
8
|
+
const loaded = readSliceContract(ref, { root });
|
|
9
|
+
const { contract, binding } = loaded;
|
|
10
|
+
const checks = { readable: 'passed', structure: 'passed', approval: 'not-checked', freshness: 'not-checked' };
|
|
11
|
+
const blockers = [];
|
|
12
|
+
if (loaded.raw.schema_version === 2) {
|
|
13
|
+
for (const [section, fields] of Object.entries(loadCompilerContract().slice_contract_required)) {
|
|
14
|
+
const target = section === 'root' ? contract : contract[section];
|
|
15
|
+
for (const field of fields) if (!target || !Object.hasOwn(target, field)) blockers.push(`missing:${section}.${field}`);
|
|
16
|
+
}
|
|
17
|
+
for (const section of ['common', 'resolution', 'backend']) {
|
|
18
|
+
if (!Array.isArray(contract[section]?.required_skills)) blockers.push(`invalid:${section}.required_skills`);
|
|
19
|
+
}
|
|
20
|
+
if (!Array.isArray(contract.work_units) || !contract.work_units.length) blockers.push('slice-contract-work-units-missing');
|
|
21
|
+
for (const unit of contract.work_units || []) {
|
|
22
|
+
if (unit.contract_id !== contract.contract_id) blockers.push(`work-unit-contract-id-mismatch:${unit.id}`);
|
|
23
|
+
if (unit.contract_version !== contract.contract_version) blockers.push(`work-unit-contract-version-mismatch:${unit.id}`);
|
|
24
|
+
if (!unit.work_unit?.primary_skill) blockers.push(`work-unit-primary-skill-missing:${unit.id}`);
|
|
25
|
+
}
|
|
26
|
+
if (blockers.length) checks.structure = 'failed';
|
|
27
|
+
}
|
|
28
|
+
if (approval_ref && !blockers.length) {
|
|
29
|
+
try {
|
|
30
|
+
const approved_slice = { ...binding, approval_ref };
|
|
31
|
+
const context = createApprovedExecutionContext(approved_slice, { root, contract, work_unit_id });
|
|
32
|
+
checks.approval = 'passed';
|
|
33
|
+
const current = evaluateContractFreshness(contract, { root, registry: loadSkillRegistry(), compilerContract: loadCompilerContract(), approved_slice, work_unit_id }, context);
|
|
34
|
+
checks.freshness = current.freshness === 'current' ? 'passed' : 'failed';
|
|
35
|
+
blockers.push(...current.reasons);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
checks.approval = 'failed';
|
|
38
|
+
blockers.push(error.code || error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { ...loaded, report: { read_only: true, ...binding, checks, approval_validity: checks.approval,
|
|
42
|
+
execution_allowed: !!approval_ref && !blockers.length && checks.freshness === 'passed', blockers } };
|
|
43
|
+
}
|
|
@@ -4,13 +4,14 @@ import path from 'node:path';
|
|
|
4
4
|
import { prepareSliceImplementationContract, migrateSliceContractV2, persistSliceDraft } from './lib/slice-contract-preparation.mjs';
|
|
5
5
|
import { renderSliceContractView, diffSliceContracts } from './lib/slice-contract-views.mjs';
|
|
6
6
|
import { parseSliceYaml, readSliceContract } from './lib/slice-contract.mjs';
|
|
7
|
+
import { inspectSliceContract } from './lib/slice-execution-preflight.mjs';
|
|
7
8
|
|
|
8
9
|
try {
|
|
9
10
|
const args=process.argv.slice(2),mode=args.shift(),options={},positional=[];
|
|
10
11
|
for(let i=0;i<args.length;i++) {
|
|
11
12
|
if(args[i]==='--json'){options.json=true;continue;}
|
|
12
13
|
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
|
+
const key=args[i].slice(2);if(!['root','input','output','unit','before-root','approval-ref'].includes(key)||!args[i+1])throw new Error(`未知或缺值参数 ${args[i]}`);
|
|
14
15
|
options[key]=args[++i];
|
|
15
16
|
}else positional.push(args[i]);
|
|
16
17
|
}
|
|
@@ -24,7 +25,10 @@ try {
|
|
|
24
25
|
if(result.report.blockers.length)process.exitCode=1;
|
|
25
26
|
}else if(mode==='view')result=renderSliceContractView(positional[0],{root,unit_id:options.unit});
|
|
26
27
|
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')
|
|
28
|
-
|
|
28
|
+
else if(mode==='verify') {
|
|
29
|
+
result=inspectSliceContract(positional[0],{root,approval_ref:options['approval-ref'],work_unit_id:options.unit}).report;
|
|
30
|
+
if(result.blockers.length)process.exitCode=1;
|
|
31
|
+
}
|
|
32
|
+
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 <项目根>] [--approval-ref <批准记录>] [--unit <工作单元>]');
|
|
29
33
|
process.stdout.write(options.json||!result.markdown?`${JSON.stringify(result,null,2)}\n`:`${result.markdown}\n`);
|
|
30
34
|
}catch(error){process.stderr.write(`${error.message}\n`);process.exitCode=1;}
|