create-yss-spec 3.5.6 → 3.5.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/bin/create-yss-spec.js +3 -1
- package/cli-core.lock.json +10 -6
- package/package.json +4 -1
- package/src/api/_sync-service.js +3 -1
- package/src/command-runner.mjs +44 -9
- package/src/content-identity.js +111 -0
- package/src/runtime-store.mjs +650 -0
- package/src/template/asset-runtime.js +4 -1
- package/src/template/instance-runtime.js +23 -7
- package/src/validation/metadata.js +8 -0
- package/template/.agents/skills/.strategic-design-skills-manifest.json +1 -1
- package/template/.agents/skills/alibaba-java-code-style/SKILL.md +30 -36
- package/template/.agents/skills/archify/SKILL.md +2 -2
- package/template/.agents/skills/archify/scripts/yss-safe-deliver.mjs +10 -6
- package/template/.agents/skills/archify/test/yss-safe-deliver.test.mjs +9 -2
- package/template/.agents/skills/code-review/SKILL.md +19 -11
- package/template/.agents/skills/code-review/references/candidate-capture.md +1 -1
- package/template/.agents/skills/code-review/references/legacy-convergence.md +3 -1
- package/template/.agents/skills/code-review/references/review-axis-checklists.md +2 -0
- package/template/.agents/skills/code-review/references/yss-review-standards.md +4 -2
- package/template/.agents/skills/competitive-intelligence/SKILL.md +32 -74
- package/template/.agents/skills/file-export-download/SKILL.md +4 -73
- package/template/.agents/skills/file-export-download/references/binary-contract-example.md +26 -0
- package/template/.agents/skills/file-export-download/references/download-call-example.md +45 -0
- package/template/.agents/skills/formily-foundation/SKILL.md +6 -105
- package/template/.agents/skills/formily-foundation/references/async-save-example.md +57 -0
- package/template/.agents/skills/formily-foundation/references/query-layout.md +50 -0
- package/template/.agents/skills/formily-linkage-effects/SKILL.md +3 -116
- package/template/.agents/skills/formily-linkage-effects/references/linkage-example.md +116 -0
- package/template/.agents/skills/formily-mode-slot-detail/SKILL.md +3 -81
- package/template/.agents/skills/formily-mode-slot-detail/references/mode-slot-example.md +81 -0
- package/template/.agents/skills/formily-step-flow/SKILL.md +3 -143
- package/template/.agents/skills/formily-step-flow/references/step-flow-example.md +143 -0
- package/template/.agents/skills/handoff/SKILL.md +3 -1
- package/template/.agents/skills/i-have-adhd/SKILL.md +2 -2
- package/template/.agents/skills/i-have-adhd/references/yss-adaptation.md +2 -2
- package/template/.agents/skills/i-have-adhd/tests/verify-integration.mjs +7 -2
- package/template/.agents/skills/implement/SKILL.md +2 -2
- package/template/.agents/skills/llm-wiki/references/transactions.md +2 -2
- package/template/.agents/skills/llm-wiki/scripts/core.mjs +40 -2
- package/template/.agents/skills/llm-wiki/scripts/transaction.mjs +30 -27
- package/template/.agents/skills/llm-wiki/scripts/v2.test.mjs +38 -2
- package/template/.agents/skills/lombok/SKILL.md +2 -0
- package/template/.agents/skills/mapstruct/SKILL.md +2 -0
- package/template/.agents/skills/tdd/SKILL.md +2 -2
- package/template/.agents/skills/theme-token-usage/SKILL.md +6 -5
- package/template/.agents/skills/wayfinder/references/chart-map.md +2 -2
- package/template/.agents/skills/wayfinder/references/ticket-types.md +1 -1
- package/template/.agents/skills/yss-api-integration/SKILL.md +13 -50
- package/template/.agents/skills/yss-api-integration/references/orval-list-example.md +38 -0
- package/template/.agents/skills/yss-application/SKILL.md +11 -5
- package/template/.agents/skills/yss-audit-log/SKILL.md +8 -6
- package/template/.agents/skills/yss-audit-log/references/README.md +5 -5
- package/template/.agents/skills/yss-cache/SKILL.md +4 -4
- package/template/.agents/skills/yss-ddd-scaffold-generator/SKILL.md +7 -8
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +13 -6
- package/template/.agents/skills/yss-ddd-scaffold-generator/references/command-examples.md +2 -0
- package/template/.agents/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +28 -1
- package/template/.agents/skills/yss-design-system/SKILL.md +2 -2
- package/template/.agents/skills/yss-distributed-id/SKILL.md +4 -4
- package/template/.agents/skills/yss-dto/SKILL.md +9 -9
- package/template/.agents/skills/yss-dto/references/README.md +4 -4
- package/template/.agents/skills/yss-dto/references/wire-validation-checklist.md +4 -4
- package/template/.agents/skills/yss-excel-mvc/SKILL.md +4 -4
- package/template/.agents/skills/yss-exception/SKILL.md +42 -48
- package/template/.agents/skills/yss-formily-schema-generator/SKILL.md +26 -170
- package/template/.agents/skills/yss-formily-schema-generator/references/workflow.md +49 -193
- package/template/.agents/skills/yss-frontend-scaffold-generator/SKILL.md +14 -10
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -6
- package/template/.agents/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +15 -1
- package/template/.agents/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.agents/skills/yss-mybatis/SKILL.md +4 -4
- package/template/.agents/skills/yss-openapi-draft-review/SKILL.md +6 -4
- package/template/.agents/skills/yss-openapi-draft-review/references/review-output.md +2 -0
- package/template/.agents/skills/yss-openapi-draft-review/references/semantic-review.md +3 -3
- package/template/.agents/skills/yss-openapi-governance/SKILL.md +5 -7
- package/template/.agents/skills/yss-openapi-governance/references/governance-output.md +2 -0
- package/template/.agents/skills/yss-openapi-governance/references/governance-workflow.md +3 -3
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +23 -21
- package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +25 -5
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +100 -4
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +35 -12
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.agents/skills/yss-product-lifecycle/references/user-decisions.md +3 -3
- package/template/.agents/skills/yss-prototype-stage/SKILL.md +1 -5
- package/template/.agents/skills/yss-repository/SKILL.md +2 -2
- package/template/.agents/skills/yss-research/SKILL.md +39 -48
- package/template/.agents/skills/yss-research/assets/competitive-evidence-template.yaml +146 -0
- package/template/.agents/skills/yss-research/references/competitive-analysis.md +76 -0
- package/template/.agents/skills/yss-research/references/competitive-analysis.schema.json +324 -0
- package/template/.agents/skills/yss-research/references/evidence-contract.md +8 -0
- package/template/.agents/skills/yss-research/scripts/lib/competitive-analysis.mjs +185 -0
- package/template/.agents/skills/yss-research/scripts/render-competitive-outputs.mjs +16 -0
- package/template/.agents/skills/yss-research/scripts/validate-research-package.mjs +6 -2
- package/template/.agents/skills/yss-research/tests/competitive-scenarios.test.mjs +270 -0
- package/template/.agents/skills/yss-resilience4j/SKILL.md +25 -31
- package/template/.agents/skills/yss-security-algorithm/SKILL.md +37 -43
- package/template/.agents/skills/yss-skill-source-index-refresh/SKILL.md +29 -36
- package/template/.agents/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
- package/template/.agents/skills/yss-stage-decision/SKILL.md +2 -5
- package/template/.agents/skills/yss-tactical-design/SKILL.md +1 -1
- package/template/.agents/skills/yss-tactical-design/references/output-template.md +3 -3
- package/template/.agents/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
- package/template/.agents/skills/yss-technical-design/SKILL.md +6 -5
- package/template/.agents/skills/yss-ui/SKILL.md +7 -1
- package/template/.agents/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +2 -2
- package/template/.agents/skills/yss-ui/references/checklist.md +38 -96
- package/template/.agents/skills/yss-ui/references/component-routing.md +3 -1
- package/template/.agents/skills/yss-ui/references/quick-recipes.md +17 -272
- package/template/.agents/skills/yss-ui/references/specialized-components.md +3 -3
- package/template/.agents/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.agents/skills/yss-up-springboot3/SKILL.md +19 -21
- package/template/.agents/skills/yss-userinfo/SKILL.md +27 -33
- package/template/.agents/skills/yss-validation/SKILL.md +26 -32
- package/template/.agents/skills/yss-web-controller/SKILL.md +11 -44
- package/template/.agents/skills/yss-web-controller/references/crud-generation.md +38 -0
- package/template/.agents/skills/ytable-usage/SKILL.md +2 -2
- package/template/.agents/skills/ytree-usage/SKILL.md +2 -80
- package/template/.agents/skills/ytree-usage/references/tree-and-confirmation-example.md +81 -0
- package/template/.codex/skills/alibaba-java-code-style/SKILL.md +30 -36
- package/template/.codex/skills/archify/SKILL.md +2 -2
- package/template/.codex/skills/archify/scripts/yss-safe-deliver.mjs +10 -6
- package/template/.codex/skills/archify/test/yss-safe-deliver.test.mjs +9 -2
- package/template/.codex/skills/code-review/SKILL.md +19 -11
- package/template/.codex/skills/code-review/references/candidate-capture.md +1 -1
- package/template/.codex/skills/code-review/references/legacy-convergence.md +3 -1
- package/template/.codex/skills/code-review/references/review-axis-checklists.md +2 -0
- package/template/.codex/skills/code-review/references/yss-review-standards.md +4 -2
- package/template/.codex/skills/competitive-intelligence/SKILL.md +32 -74
- package/template/.codex/skills/file-export-download/SKILL.md +4 -73
- package/template/.codex/skills/file-export-download/references/binary-contract-example.md +26 -0
- package/template/.codex/skills/file-export-download/references/download-call-example.md +45 -0
- package/template/.codex/skills/formily-foundation/SKILL.md +6 -105
- package/template/.codex/skills/formily-foundation/references/async-save-example.md +57 -0
- package/template/.codex/skills/formily-foundation/references/query-layout.md +50 -0
- package/template/.codex/skills/formily-linkage-effects/SKILL.md +3 -116
- package/template/.codex/skills/formily-linkage-effects/references/linkage-example.md +116 -0
- package/template/.codex/skills/formily-mode-slot-detail/SKILL.md +3 -81
- package/template/.codex/skills/formily-mode-slot-detail/references/mode-slot-example.md +81 -0
- package/template/.codex/skills/formily-step-flow/SKILL.md +3 -143
- package/template/.codex/skills/formily-step-flow/references/step-flow-example.md +143 -0
- package/template/.codex/skills/handoff/SKILL.md +3 -1
- package/template/.codex/skills/i-have-adhd/SKILL.md +2 -2
- package/template/.codex/skills/i-have-adhd/references/yss-adaptation.md +2 -2
- package/template/.codex/skills/i-have-adhd/tests/verify-integration.mjs +7 -2
- package/template/.codex/skills/implement/SKILL.md +2 -2
- package/template/.codex/skills/llm-wiki/references/transactions.md +2 -2
- package/template/.codex/skills/llm-wiki/scripts/core.mjs +40 -2
- package/template/.codex/skills/llm-wiki/scripts/transaction.mjs +30 -27
- package/template/.codex/skills/llm-wiki/scripts/v2.test.mjs +38 -2
- package/template/.codex/skills/lombok/SKILL.md +2 -0
- package/template/.codex/skills/mapstruct/SKILL.md +2 -0
- package/template/.codex/skills/product-design/references/communication-protocol.md +3 -3
- package/template/.codex/skills/product-design/references/critical-overrides.md +4 -5
- package/template/.codex/skills/product-design/skills/audit/SKILL.md +2 -4
- package/template/.codex/skills/product-design/skills/get-context/SKILL.md +6 -8
- package/template/.codex/skills/product-design/skills/ideate/SKILL.md +3 -3
- package/template/.codex/skills/product-design/skills/image-to-code/SKILL.md +3 -5
- package/template/.codex/skills/product-design/skills/index/SKILL.md +1 -1
- package/template/.codex/skills/product-design/skills/prototype/SKILL.md +2 -4
- package/template/.codex/skills/product-design/skills/research/SKILL.md +4 -6
- package/template/.codex/skills/product-design/skills/research/assets/research-brief-template.md +3 -3
- package/template/.codex/skills/product-design/skills/share/SKILL.md +2 -4
- package/template/.codex/skills/product-design/skills/url-to-code/SKILL.md +2 -4
- package/template/.codex/skills/product-design/skills/user-context/SKILL.md +1 -1
- package/template/.codex/skills/tdd/SKILL.md +2 -2
- package/template/.codex/skills/theme-token-usage/SKILL.md +6 -5
- package/template/.codex/skills/wayfinder/references/chart-map.md +2 -2
- package/template/.codex/skills/wayfinder/references/ticket-types.md +1 -1
- package/template/.codex/skills/yss-api-integration/SKILL.md +13 -50
- package/template/.codex/skills/yss-api-integration/references/orval-list-example.md +38 -0
- package/template/.codex/skills/yss-application/SKILL.md +11 -5
- package/template/.codex/skills/yss-audit-log/SKILL.md +8 -6
- package/template/.codex/skills/yss-audit-log/references/README.md +5 -5
- package/template/.codex/skills/yss-cache/SKILL.md +4 -4
- package/template/.codex/skills/yss-ddd-scaffold-generator/SKILL.md +7 -8
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +13 -6
- package/template/.codex/skills/yss-ddd-scaffold-generator/references/command-examples.md +2 -0
- package/template/.codex/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +28 -1
- package/template/.codex/skills/yss-design-system/SKILL.md +2 -2
- package/template/.codex/skills/yss-distributed-id/SKILL.md +4 -4
- package/template/.codex/skills/yss-dto/SKILL.md +9 -9
- package/template/.codex/skills/yss-dto/references/README.md +4 -4
- package/template/.codex/skills/yss-dto/references/wire-validation-checklist.md +4 -4
- package/template/.codex/skills/yss-excel-mvc/SKILL.md +4 -4
- package/template/.codex/skills/yss-exception/SKILL.md +42 -48
- package/template/.codex/skills/yss-formily-schema-generator/SKILL.md +26 -170
- package/template/.codex/skills/yss-formily-schema-generator/references/workflow.md +49 -193
- package/template/.codex/skills/yss-frontend-scaffold-generator/SKILL.md +14 -10
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -6
- package/template/.codex/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +15 -1
- package/template/.codex/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.codex/skills/yss-mybatis/SKILL.md +4 -4
- package/template/.codex/skills/yss-openapi-draft-review/SKILL.md +6 -4
- package/template/.codex/skills/yss-openapi-draft-review/references/review-output.md +2 -0
- package/template/.codex/skills/yss-openapi-draft-review/references/semantic-review.md +3 -3
- package/template/.codex/skills/yss-openapi-governance/SKILL.md +5 -7
- package/template/.codex/skills/yss-openapi-governance/references/governance-output.md +2 -0
- package/template/.codex/skills/yss-openapi-governance/references/governance-workflow.md +3 -3
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +23 -21
- package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +25 -5
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +100 -4
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +35 -12
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.codex/skills/yss-product-lifecycle/references/user-decisions.md +3 -3
- package/template/.codex/skills/yss-prototype-stage/SKILL.md +1 -5
- package/template/.codex/skills/yss-repository/SKILL.md +2 -2
- package/template/.codex/skills/yss-research/SKILL.md +39 -48
- package/template/.codex/skills/yss-research/assets/competitive-evidence-template.yaml +146 -0
- package/template/.codex/skills/yss-research/references/competitive-analysis.md +76 -0
- package/template/.codex/skills/yss-research/references/competitive-analysis.schema.json +324 -0
- package/template/.codex/skills/yss-research/references/evidence-contract.md +8 -0
- package/template/.codex/skills/yss-research/scripts/lib/competitive-analysis.mjs +185 -0
- package/template/.codex/skills/yss-research/scripts/render-competitive-outputs.mjs +16 -0
- package/template/.codex/skills/yss-research/scripts/validate-research-package.mjs +6 -2
- package/template/.codex/skills/yss-research/tests/competitive-scenarios.test.mjs +270 -0
- package/template/.codex/skills/yss-resilience4j/SKILL.md +25 -31
- package/template/.codex/skills/yss-security-algorithm/SKILL.md +37 -43
- package/template/.codex/skills/yss-skill-source-index-refresh/SKILL.md +29 -36
- package/template/.codex/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
- package/template/.codex/skills/yss-stage-decision/SKILL.md +2 -5
- package/template/.codex/skills/yss-tactical-design/SKILL.md +1 -1
- package/template/.codex/skills/yss-tactical-design/references/output-template.md +3 -3
- package/template/.codex/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
- package/template/.codex/skills/yss-technical-design/SKILL.md +6 -5
- package/template/.codex/skills/yss-ui/SKILL.md +7 -1
- package/template/.codex/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +2 -2
- package/template/.codex/skills/yss-ui/references/checklist.md +38 -96
- package/template/.codex/skills/yss-ui/references/component-routing.md +3 -1
- package/template/.codex/skills/yss-ui/references/quick-recipes.md +17 -272
- package/template/.codex/skills/yss-ui/references/specialized-components.md +3 -3
- package/template/.codex/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.codex/skills/yss-up-springboot3/SKILL.md +19 -21
- package/template/.codex/skills/yss-userinfo/SKILL.md +27 -33
- package/template/.codex/skills/yss-validation/SKILL.md +26 -32
- package/template/.codex/skills/yss-web-controller/SKILL.md +11 -44
- package/template/.codex/skills/yss-web-controller/references/crud-generation.md +38 -0
- package/template/.codex/skills/ytable-usage/SKILL.md +2 -2
- package/template/.codex/skills/ytree-usage/SKILL.md +2 -80
- package/template/.codex/skills/ytree-usage/references/tree-and-confirmation-example.md +81 -0
- package/template/.cursor/skills/alibaba-java-code-style/SKILL.md +30 -36
- package/template/.cursor/skills/archify/SKILL.md +2 -2
- package/template/.cursor/skills/archify/scripts/yss-safe-deliver.mjs +10 -6
- package/template/.cursor/skills/archify/test/yss-safe-deliver.test.mjs +9 -2
- package/template/.cursor/skills/code-review/SKILL.md +19 -11
- package/template/.cursor/skills/code-review/references/candidate-capture.md +1 -1
- package/template/.cursor/skills/code-review/references/legacy-convergence.md +3 -1
- package/template/.cursor/skills/code-review/references/review-axis-checklists.md +2 -0
- package/template/.cursor/skills/code-review/references/yss-review-standards.md +4 -2
- package/template/.cursor/skills/competitive-intelligence/SKILL.md +32 -74
- package/template/.cursor/skills/file-export-download/SKILL.md +4 -73
- package/template/.cursor/skills/file-export-download/references/binary-contract-example.md +26 -0
- package/template/.cursor/skills/file-export-download/references/download-call-example.md +45 -0
- package/template/.cursor/skills/formily-foundation/SKILL.md +6 -105
- package/template/.cursor/skills/formily-foundation/references/async-save-example.md +57 -0
- package/template/.cursor/skills/formily-foundation/references/query-layout.md +50 -0
- package/template/.cursor/skills/formily-linkage-effects/SKILL.md +3 -116
- package/template/.cursor/skills/formily-linkage-effects/references/linkage-example.md +116 -0
- package/template/.cursor/skills/formily-mode-slot-detail/SKILL.md +3 -81
- package/template/.cursor/skills/formily-mode-slot-detail/references/mode-slot-example.md +81 -0
- package/template/.cursor/skills/formily-step-flow/SKILL.md +3 -143
- package/template/.cursor/skills/formily-step-flow/references/step-flow-example.md +143 -0
- package/template/.cursor/skills/handoff/SKILL.md +3 -1
- package/template/.cursor/skills/i-have-adhd/SKILL.md +2 -2
- package/template/.cursor/skills/i-have-adhd/references/yss-adaptation.md +2 -2
- package/template/.cursor/skills/i-have-adhd/tests/verify-integration.mjs +7 -2
- package/template/.cursor/skills/implement/SKILL.md +2 -2
- package/template/.cursor/skills/llm-wiki/references/transactions.md +2 -2
- package/template/.cursor/skills/llm-wiki/scripts/core.mjs +40 -2
- package/template/.cursor/skills/llm-wiki/scripts/transaction.mjs +30 -27
- package/template/.cursor/skills/llm-wiki/scripts/v2.test.mjs +38 -2
- package/template/.cursor/skills/lombok/SKILL.md +2 -0
- package/template/.cursor/skills/mapstruct/SKILL.md +2 -0
- package/template/.cursor/skills/tdd/SKILL.md +2 -2
- package/template/.cursor/skills/theme-token-usage/SKILL.md +6 -5
- package/template/.cursor/skills/wayfinder/references/chart-map.md +2 -2
- package/template/.cursor/skills/wayfinder/references/ticket-types.md +1 -1
- package/template/.cursor/skills/yss-api-integration/SKILL.md +13 -50
- package/template/.cursor/skills/yss-api-integration/references/orval-list-example.md +38 -0
- package/template/.cursor/skills/yss-application/SKILL.md +11 -5
- package/template/.cursor/skills/yss-audit-log/SKILL.md +8 -6
- package/template/.cursor/skills/yss-audit-log/references/README.md +5 -5
- package/template/.cursor/skills/yss-cache/SKILL.md +4 -4
- package/template/.cursor/skills/yss-ddd-scaffold-generator/SKILL.md +7 -8
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +13 -6
- package/template/.cursor/skills/yss-ddd-scaffold-generator/references/command-examples.md +2 -0
- package/template/.cursor/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +28 -1
- package/template/.cursor/skills/yss-design-system/SKILL.md +2 -2
- package/template/.cursor/skills/yss-distributed-id/SKILL.md +4 -4
- package/template/.cursor/skills/yss-dto/SKILL.md +9 -9
- package/template/.cursor/skills/yss-dto/references/README.md +4 -4
- package/template/.cursor/skills/yss-dto/references/wire-validation-checklist.md +4 -4
- package/template/.cursor/skills/yss-excel-mvc/SKILL.md +4 -4
- package/template/.cursor/skills/yss-exception/SKILL.md +42 -48
- package/template/.cursor/skills/yss-formily-schema-generator/SKILL.md +26 -170
- package/template/.cursor/skills/yss-formily-schema-generator/references/workflow.md +49 -193
- package/template/.cursor/skills/yss-frontend-scaffold-generator/SKILL.md +14 -10
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -6
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +15 -1
- package/template/.cursor/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.cursor/skills/yss-mybatis/SKILL.md +4 -4
- package/template/.cursor/skills/yss-openapi-draft-review/SKILL.md +6 -4
- package/template/.cursor/skills/yss-openapi-draft-review/references/review-output.md +2 -0
- package/template/.cursor/skills/yss-openapi-draft-review/references/semantic-review.md +3 -3
- package/template/.cursor/skills/yss-openapi-governance/SKILL.md +5 -7
- package/template/.cursor/skills/yss-openapi-governance/references/governance-output.md +2 -0
- package/template/.cursor/skills/yss-openapi-governance/references/governance-workflow.md +3 -3
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +23 -21
- package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +25 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +100 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +35 -12
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.cursor/skills/yss-product-lifecycle/references/user-decisions.md +3 -3
- package/template/.cursor/skills/yss-prototype-stage/SKILL.md +1 -5
- package/template/.cursor/skills/yss-repository/SKILL.md +2 -2
- package/template/.cursor/skills/yss-research/SKILL.md +39 -48
- package/template/.cursor/skills/yss-research/assets/competitive-evidence-template.yaml +146 -0
- package/template/.cursor/skills/yss-research/references/competitive-analysis.md +76 -0
- package/template/.cursor/skills/yss-research/references/competitive-analysis.schema.json +324 -0
- package/template/.cursor/skills/yss-research/references/evidence-contract.md +8 -0
- package/template/.cursor/skills/yss-research/scripts/lib/competitive-analysis.mjs +185 -0
- package/template/.cursor/skills/yss-research/scripts/render-competitive-outputs.mjs +16 -0
- package/template/.cursor/skills/yss-research/scripts/validate-research-package.mjs +6 -2
- package/template/.cursor/skills/yss-research/tests/competitive-scenarios.test.mjs +270 -0
- package/template/.cursor/skills/yss-resilience4j/SKILL.md +25 -31
- package/template/.cursor/skills/yss-security-algorithm/SKILL.md +37 -43
- package/template/.cursor/skills/yss-skill-source-index-refresh/SKILL.md +29 -36
- package/template/.cursor/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +2 -5
- package/template/.cursor/skills/yss-tactical-design/SKILL.md +1 -1
- package/template/.cursor/skills/yss-tactical-design/references/output-template.md +3 -3
- package/template/.cursor/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
- package/template/.cursor/skills/yss-technical-design/SKILL.md +6 -5
- package/template/.cursor/skills/yss-ui/SKILL.md +7 -1
- package/template/.cursor/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +2 -2
- package/template/.cursor/skills/yss-ui/references/checklist.md +38 -96
- package/template/.cursor/skills/yss-ui/references/component-routing.md +3 -1
- package/template/.cursor/skills/yss-ui/references/quick-recipes.md +17 -272
- package/template/.cursor/skills/yss-ui/references/specialized-components.md +3 -3
- package/template/.cursor/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.cursor/skills/yss-up-springboot3/SKILL.md +19 -21
- package/template/.cursor/skills/yss-userinfo/SKILL.md +27 -33
- package/template/.cursor/skills/yss-validation/SKILL.md +26 -32
- package/template/.cursor/skills/yss-web-controller/SKILL.md +11 -44
- package/template/.cursor/skills/yss-web-controller/references/crud-generation.md +38 -0
- package/template/.cursor/skills/ytable-usage/SKILL.md +2 -2
- package/template/.cursor/skills/ytree-usage/SKILL.md +2 -80
- package/template/.cursor/skills/ytree-usage/references/tree-and-confirmation-example.md +81 -0
- package/template/.pi/skills/alibaba-java-code-style/SKILL.md +30 -36
- package/template/.pi/skills/archify/SKILL.md +2 -2
- package/template/.pi/skills/archify/scripts/yss-safe-deliver.mjs +10 -6
- package/template/.pi/skills/archify/test/yss-safe-deliver.test.mjs +9 -2
- package/template/.pi/skills/code-review/SKILL.md +19 -11
- package/template/.pi/skills/code-review/references/candidate-capture.md +1 -1
- package/template/.pi/skills/code-review/references/legacy-convergence.md +3 -1
- package/template/.pi/skills/code-review/references/review-axis-checklists.md +2 -0
- package/template/.pi/skills/code-review/references/yss-review-standards.md +4 -2
- package/template/.pi/skills/competitive-intelligence/SKILL.md +32 -74
- package/template/.pi/skills/file-export-download/SKILL.md +4 -73
- package/template/.pi/skills/file-export-download/references/binary-contract-example.md +26 -0
- package/template/.pi/skills/file-export-download/references/download-call-example.md +45 -0
- package/template/.pi/skills/formily-foundation/SKILL.md +6 -105
- package/template/.pi/skills/formily-foundation/references/async-save-example.md +57 -0
- package/template/.pi/skills/formily-foundation/references/query-layout.md +50 -0
- package/template/.pi/skills/formily-linkage-effects/SKILL.md +3 -116
- package/template/.pi/skills/formily-linkage-effects/references/linkage-example.md +116 -0
- package/template/.pi/skills/formily-mode-slot-detail/SKILL.md +3 -81
- package/template/.pi/skills/formily-mode-slot-detail/references/mode-slot-example.md +81 -0
- package/template/.pi/skills/formily-step-flow/SKILL.md +3 -143
- package/template/.pi/skills/formily-step-flow/references/step-flow-example.md +143 -0
- package/template/.pi/skills/handoff/SKILL.md +3 -1
- package/template/.pi/skills/i-have-adhd/SKILL.md +2 -2
- package/template/.pi/skills/i-have-adhd/references/yss-adaptation.md +2 -2
- package/template/.pi/skills/i-have-adhd/tests/verify-integration.mjs +7 -2
- package/template/.pi/skills/implement/SKILL.md +2 -2
- package/template/.pi/skills/llm-wiki/references/transactions.md +2 -2
- package/template/.pi/skills/llm-wiki/scripts/core.mjs +40 -2
- package/template/.pi/skills/llm-wiki/scripts/transaction.mjs +30 -27
- package/template/.pi/skills/llm-wiki/scripts/v2.test.mjs +38 -2
- package/template/.pi/skills/lombok/SKILL.md +2 -0
- package/template/.pi/skills/mapstruct/SKILL.md +2 -0
- package/template/.pi/skills/tdd/SKILL.md +2 -2
- package/template/.pi/skills/theme-token-usage/SKILL.md +6 -5
- package/template/.pi/skills/wayfinder/references/chart-map.md +2 -2
- package/template/.pi/skills/wayfinder/references/ticket-types.md +1 -1
- package/template/.pi/skills/yss-api-integration/SKILL.md +13 -50
- package/template/.pi/skills/yss-api-integration/references/orval-list-example.md +38 -0
- package/template/.pi/skills/yss-application/SKILL.md +11 -5
- package/template/.pi/skills/yss-audit-log/SKILL.md +8 -6
- package/template/.pi/skills/yss-audit-log/references/README.md +5 -5
- package/template/.pi/skills/yss-cache/SKILL.md +4 -4
- package/template/.pi/skills/yss-ddd-scaffold-generator/SKILL.md +7 -8
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/USAGE_EXAMPLES.md +13 -6
- package/template/.pi/skills/yss-ddd-scaffold-generator/references/command-examples.md +2 -0
- package/template/.pi/skills/yss-ddd-scaffold-generator/scripts/scaffold-generator.test.mjs +28 -1
- package/template/.pi/skills/yss-design-system/SKILL.md +2 -2
- package/template/.pi/skills/yss-distributed-id/SKILL.md +4 -4
- package/template/.pi/skills/yss-dto/SKILL.md +9 -9
- package/template/.pi/skills/yss-dto/references/README.md +4 -4
- package/template/.pi/skills/yss-dto/references/wire-validation-checklist.md +4 -4
- package/template/.pi/skills/yss-excel-mvc/SKILL.md +4 -4
- package/template/.pi/skills/yss-exception/SKILL.md +42 -48
- package/template/.pi/skills/yss-formily-schema-generator/SKILL.md +26 -170
- package/template/.pi/skills/yss-formily-schema-generator/references/workflow.md +49 -193
- package/template/.pi/skills/yss-frontend-scaffold-generator/SKILL.md +14 -10
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -6
- package/template/.pi/skills/yss-implementation-contract-compiler/references/yss-skill-execution-result.md +15 -1
- package/template/.pi/skills/yss-layered-mvc-scaffold-generator/SKILL.md +1 -1
- package/template/.pi/skills/yss-mybatis/SKILL.md +4 -4
- package/template/.pi/skills/yss-openapi-draft-review/SKILL.md +6 -4
- package/template/.pi/skills/yss-openapi-draft-review/references/review-output.md +2 -0
- package/template/.pi/skills/yss-openapi-draft-review/references/semantic-review.md +3 -3
- package/template/.pi/skills/yss-openapi-governance/SKILL.md +5 -7
- package/template/.pi/skills/yss-openapi-governance/references/governance-output.md +2 -0
- package/template/.pi/skills/yss-openapi-governance/references/governance-workflow.md +3 -3
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +23 -21
- package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +1 -1
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +25 -5
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +100 -4
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +35 -12
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +3 -1
- package/template/.pi/skills/yss-product-lifecycle/references/user-decisions.md +3 -3
- package/template/.pi/skills/yss-prototype-stage/SKILL.md +1 -5
- package/template/.pi/skills/yss-repository/SKILL.md +2 -2
- package/template/.pi/skills/yss-research/SKILL.md +39 -48
- package/template/.pi/skills/yss-research/assets/competitive-evidence-template.yaml +146 -0
- package/template/.pi/skills/yss-research/references/competitive-analysis.md +76 -0
- package/template/.pi/skills/yss-research/references/competitive-analysis.schema.json +324 -0
- package/template/.pi/skills/yss-research/references/evidence-contract.md +8 -0
- package/template/.pi/skills/yss-research/scripts/lib/competitive-analysis.mjs +185 -0
- package/template/.pi/skills/yss-research/scripts/render-competitive-outputs.mjs +16 -0
- package/template/.pi/skills/yss-research/scripts/validate-research-package.mjs +6 -2
- package/template/.pi/skills/yss-research/tests/competitive-scenarios.test.mjs +270 -0
- package/template/.pi/skills/yss-resilience4j/SKILL.md +25 -31
- package/template/.pi/skills/yss-security-algorithm/SKILL.md +37 -43
- package/template/.pi/skills/yss-skill-source-index-refresh/SKILL.md +29 -36
- package/template/.pi/skills/yss-skill-source-index-refresh/references/backend-component-platform-compatibility.md +1 -1
- package/template/.pi/skills/yss-stage-decision/SKILL.md +2 -5
- package/template/.pi/skills/yss-tactical-design/SKILL.md +1 -1
- package/template/.pi/skills/yss-tactical-design/references/output-template.md +3 -3
- package/template/.pi/skills/yss-tactical-design/references/tactical-design-contract.md +1 -1
- package/template/.pi/skills/yss-technical-design/SKILL.md +6 -5
- package/template/.pi/skills/yss-ui/SKILL.md +7 -1
- package/template/.pi/skills/yss-ui/assets/docs/guide/gitlab-ci-integration.md +2 -2
- package/template/.pi/skills/yss-ui/references/checklist.md +38 -96
- package/template/.pi/skills/yss-ui/references/component-routing.md +3 -1
- package/template/.pi/skills/yss-ui/references/quick-recipes.md +17 -272
- package/template/.pi/skills/yss-ui/references/specialized-components.md +3 -3
- package/template/.pi/skills/yss-ui-business-page-generation/SKILL.md +1 -1
- package/template/.pi/skills/yss-up-springboot3/SKILL.md +19 -21
- package/template/.pi/skills/yss-userinfo/SKILL.md +27 -33
- package/template/.pi/skills/yss-validation/SKILL.md +26 -32
- package/template/.pi/skills/yss-web-controller/SKILL.md +11 -44
- package/template/.pi/skills/yss-web-controller/references/crud-generation.md +38 -0
- package/template/.pi/skills/ytable-usage/SKILL.md +2 -2
- package/template/.pi/skills/ytree-usage/SKILL.md +2 -80
- package/template/.pi/skills/ytree-usage/references/tree-and-confirmation-example.md +81 -0
- package/template/.template-spec/agents/backend-architecture-profiles.md +1 -1
- package/template/.template-spec/agents/digital-human-roles.md +5 -1
- package/template/.template-spec/agents/digital-human-roles.yaml +49 -1
- package/template/.template-spec/engineering/backend-platforms.json +1 -1
- package/template/.template-spec/plan/templates/competitive-analysis-template.md +38 -36
- package/template/.template-spec/plan/templates/competitive-matrix-template.md +37 -17
- package/template/.template-spec/process/document-writing.md +22 -3
- package/template/.template-spec/process/harness-process-tailoring.md +5 -1
- package/template/.template-spec/process/implementation-repo-integration.md +26 -8
- package/template/.template-spec/process/research-completion.md +2 -0
- package/template/.template-spec/process/runtime-storage.md +78 -0
- package/template/.template-spec/process/schemas/approval-context.schema.json +67 -0
- package/template/.template-spec/process/schemas/approval-record.schema.json +179 -16
- package/template/.template-spec/process/schemas/backend-delivery.schema.json +16 -0
- package/template/.template-spec/process/schemas/delivery-preflight-input.schema.json +4 -0
- package/template/.template-spec/process/schemas/digital-human-task-package.schema.json +236 -1
- package/template/.template-spec/process/schemas/implementation-repository-preparation-result.schema.json +335 -52
- package/template/.template-spec/process/schemas/lifecycle-checkpoint.schema.json +11 -0
- package/template/.template-spec/process/schemas/project-scaffold-contract.schema.json +3 -0
- package/template/.template-spec/process/schemas/review-bundle.schema.json +347 -35
- package/template/.template-spec/process/schemas/strategic-design-handoff-v3.schema.json +504 -0
- package/template/.template-spec/process/schemas/strategic-design-handoff-v4.schema.json +544 -8
- package/template/.template-spec/process/schemas/strategic-design-handoff-v5.schema.json +616 -0
- package/template/.template-spec/process/schemas/strategic-design-handoff.schema.json +616 -0
- package/template/.template-spec/process/schemas/strategic-handoff-domain-strategy-v3.schema.json +712 -109
- package/template/.template-spec/process/schemas/strategic-handoff-domain-strategy.schema.json +595 -90
- package/template/.template-spec/process/schemas/strategic-handoff-export-v2.schema.json +448 -0
- package/template/.template-spec/process/schemas/strategic-handoff-export.schema.json +392 -0
- package/template/.template-spec/process/schemas/strategic-handoff-stage-decision-package-v3.schema.json +556 -83
- package/template/.template-spec/process/schemas/strategic-handoff-stage-decision-package.schema.json +418 -57
- package/template/.template-spec/process/subagent-collaboration.md +7 -1
- package/template/.template-spec/templates/approval-record-template.json +7 -3
- package/template/.template-spec/templates/approval-record-template.yaml +8 -2
- package/template/.template-spec/templates/examples/lifecycle-writing-examples.md +92 -2
- package/template/.template-spec/templates/retro-report-template.md +10 -3
- package/template/.template-spec/templates/review-bundle-template.yaml +19 -10
- package/template/AGENTS.md +4 -3
- package/template/CONTEXT.md +2 -2
- package/template/README.md +2 -2
- package/template/scripts/advance-maintenance-iteration +51 -0
- package/template/scripts/lib/approval-checkpoint-discovery.mjs +26 -0
- package/template/scripts/lib/approval-consumption.mjs +35 -0
- package/template/scripts/lib/approval-current.mjs +111 -0
- package/template/scripts/lib/approval-record-io.mjs +56 -0
- package/template/scripts/lib/approval-record.mjs +79 -184
- package/template/scripts/lib/approved-execution-context.mjs +8 -6
- package/template/scripts/lib/asset-transactions.mjs +5 -1
- package/template/scripts/lib/backend-delivery.mjs +1 -1
- package/template/scripts/lib/backend-review.mjs +2 -1
- package/template/scripts/lib/backend-scaffold-prerequisites.mjs +3 -2
- package/template/scripts/lib/business-ticket-lifecycle.mjs +2 -1
- package/template/scripts/lib/checkpoint-commit.mjs +50 -0
- package/template/scripts/lib/command-runner.mjs +44 -9
- package/template/scripts/lib/content-identity.mjs +273 -0
- package/template/scripts/lib/delivery-preflight.mjs +2 -1
- package/template/scripts/lib/digital-human-roles.mjs +12 -5
- package/template/scripts/lib/drift-report.mjs +110 -0
- package/template/scripts/lib/existing-backend-architecture.mjs +4 -1
- package/template/scripts/lib/lifecycle-context-query.mjs +172 -11
- package/template/scripts/lib/lifecycle-controls.mjs +9 -19
- package/template/scripts/lib/lifecycle-presentation.mjs +5 -0
- package/template/scripts/lib/lifecycle-status.mjs +63 -12
- package/template/scripts/lib/lifecycle-transition.mjs +2 -1
- package/template/scripts/lib/maintenance-candidate.mjs +24 -30
- package/template/scripts/lib/maintenance-research.mjs +52 -8
- package/template/scripts/lib/maintenance-storage.mjs +81 -0
- package/template/scripts/lib/plan-spec-entry.mjs +2 -1
- package/template/scripts/lib/project-governance.mjs +2 -1
- package/template/scripts/lib/read-only-intake.mjs +44 -9
- package/template/scripts/lib/review-capabilities.mjs +234 -0
- package/template/scripts/lib/review-package.mjs +100 -0
- package/template/scripts/lib/runtime-store.mjs +650 -0
- package/template/scripts/lib/skill-registry.mjs +4 -4
- package/template/scripts/lib/strategic-handoff.mjs +22 -9
- package/template/scripts/lib/task-package.mjs +47 -16
- package/template/scripts/lifecycle-status +26 -1
- package/template/scripts/maintenance-path +11 -0
- package/template/scripts/prepare-review-package +8 -0
- package/template/scripts/project-ci +13 -3
- package/template/scripts/query-lifecycle-context +8 -1
- package/template/scripts/run-read-only-intake +2 -2
- package/template/scripts/runtime-store +40 -0
- package/template/scripts/verify-approval-record +21 -13
- package/template/scripts/verify-lifecycle-checkpoint +7 -3
- package/template/scripts/verify-project-instance +21 -1
- package/template/skills-lock.json +62 -61
- package/template.snapshot.json +4 -4
- package/vendor/cli-core/cli.mjs +2 -0
- package/vendor/cli-core/command-runner.mjs +44 -9
- package/vendor/cli-core/package.json +1 -1
- package/vendor/cli-core/runtime-store.mjs +650 -0
- package/vendor/cli-core/scaffold.mjs +4 -2
|
@@ -3,24 +3,66 @@ import path from 'node:path';
|
|
|
3
3
|
import {spawnSync} from 'node:child_process';
|
|
4
4
|
import {safe, hash, parse, ensure} from './strategic-handoff-io.mjs';
|
|
5
5
|
import {assertUserDecisionRequirement} from './user-decision.mjs';
|
|
6
|
+
import {resolveMaintenanceOutput, resolveMaintenanceReference, maintenanceReference} from './maintenance-storage.mjs';
|
|
6
7
|
|
|
7
8
|
export const RESEARCH_VALIDATOR = '.agents/skills/yss-research/scripts/validate-research-package.mjs';
|
|
8
|
-
const
|
|
9
|
+
const file = (root, ref) => ref.startsWith('maintenance:') ? resolveMaintenanceReference(ref, {root}) : safe(root, ref);
|
|
10
|
+
const binding = (root, ref) => ({ref, digest: hash(fs.readFileSync(file(root, ref)))});
|
|
9
11
|
function readBound(root, bound) {
|
|
10
12
|
ensure(bound && binding(root, bound.ref).digest === bound.digest, 'research-evidence-stale');
|
|
11
|
-
return fs.readFileSync(
|
|
13
|
+
return fs.readFileSync(file(root, bound.ref));
|
|
12
14
|
}
|
|
13
15
|
function execute(root, brief, evidence) {
|
|
14
|
-
return spawnSync(process.execPath, [safe(root, RESEARCH_VALIDATOR),
|
|
16
|
+
return spawnSync(process.execPath, [safe(root, RESEARCH_VALIDATOR), file(root, brief), file(root, evidence)], {
|
|
15
17
|
cwd: root, encoding: 'utf8', timeout: 30000, maxBuffer: 2 * 1024 * 1024,
|
|
16
18
|
env: {...process.env, NODE_OPTIONS: '', NODE_PATH: ''},
|
|
17
19
|
});
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
function competitiveBindings(root, evidence) {
|
|
23
|
+
const evidenceFile = file(root, evidence);
|
|
24
|
+
if (!Object.hasOwn(parse(fs.readFileSync(evidenceFile)), 'competitive_analysis')) return [];
|
|
25
|
+
// Load only for this extension: slim installations and legacy records need no Skill dependency.
|
|
26
|
+
// Reuse the Skill's authoritative dependency list instead of defining a second list here.
|
|
27
|
+
const moduleFile = safe(root, '.agents/skills/yss-research/scripts/lib/competitive-analysis.mjs');
|
|
28
|
+
const query = `import {pathToFileURL} from 'node:url';
|
|
29
|
+
const {competitiveVerificationFiles} = await import(pathToFileURL(process.argv[1]).href);
|
|
30
|
+
process.stdout.write(JSON.stringify(competitiveVerificationFiles(process.argv[2], {root: process.argv[3]})));`;
|
|
31
|
+
const result = spawnSync(process.execPath, ['--input-type=module', '-e', query, moduleFile, evidenceFile, root], {
|
|
32
|
+
cwd: root, encoding: 'utf8', timeout: 30000, maxBuffer: 2 * 1024 * 1024,
|
|
33
|
+
env: {...process.env, NODE_OPTIONS: '', NODE_PATH: ''},
|
|
34
|
+
});
|
|
35
|
+
ensure(!result.error && result.status === 0, `research-competitive-inputs-invalid: ${result.stderr?.trim() || result.error?.message || result.status}`);
|
|
36
|
+
return JSON.parse(result.stdout)
|
|
37
|
+
.map(filename => {
|
|
38
|
+
const relative = path.relative(root, filename).split(path.sep).join('/');
|
|
39
|
+
return binding(root, relative === '..' || relative.startsWith('../') ? maintenanceReference(filename, {root}) : relative);
|
|
40
|
+
})
|
|
41
|
+
.sort((a, b) => a.ref.localeCompare(b.ref));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function inputBindings(inputs) {
|
|
45
|
+
return [inputs.brief, inputs.evidence, inputs.validator, ...(inputs.competitive || [])];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function verifyCompetitiveBindings(root, record) {
|
|
49
|
+
const expected = competitiveBindings(root, record.inputs.evidence.ref);
|
|
50
|
+
if (expected.length) {
|
|
51
|
+
ensure(Array.isArray(record.inputs.competitive), 'research-competitive-bindings-missing');
|
|
52
|
+
for (const bound of record.inputs.competitive) readBound(root, bound);
|
|
53
|
+
ensure(JSON.stringify(record.inputs.competitive) === JSON.stringify(expected), 'research-competitive-bindings-mismatch');
|
|
54
|
+
} else {
|
|
55
|
+
ensure(record.inputs.competitive === undefined, 'research-competitive-bindings-unexpected');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
20
59
|
/** Capture actual validator output; never overwrite a historical run. */
|
|
21
60
|
export function recordResearchVerification(root, brief, evidence, output) {
|
|
22
61
|
const inputs = {brief: binding(root, brief), evidence: binding(root, evidence), validator: binding(root, RESEARCH_VALIDATOR)};
|
|
23
|
-
const
|
|
62
|
+
const competitive = competitiveBindings(root, evidence);
|
|
63
|
+
if (competitive.length) inputs.competitive = competitive;
|
|
64
|
+
output = output.startsWith('maintenance:') ? output : maintenanceReference(output, {root});
|
|
65
|
+
const dir = resolveMaintenanceOutput(output, {root});
|
|
24
66
|
ensure(!fs.existsSync(dir), '研究验证输出目录必须不存在');
|
|
25
67
|
const started_at = new Date().toISOString(), result = execute(root, brief, evidence);
|
|
26
68
|
fs.mkdirSync(dir, {recursive: true});
|
|
@@ -29,31 +71,33 @@ export function recordResearchVerification(root, brief, evidence, output) {
|
|
|
29
71
|
const record = {schema_version: 1, kind: 'maintenance-research-verification', inputs,
|
|
30
72
|
command: ['node', RESEARCH_VALIDATOR, brief, evidence], started_at, completed_at: new Date().toISOString(),
|
|
31
73
|
exit_code: result.status, stdout: binding(root, `${output}/stdout.log`), stderr: binding(root, `${output}/stderr.log`)};
|
|
32
|
-
for (const value of
|
|
74
|
+
for (const value of inputBindings(inputs)) readBound(root, value);
|
|
33
75
|
fs.writeFileSync(path.join(dir, 'verification.json'), JSON.stringify(record, null, 2) + '\n');
|
|
34
76
|
return {binding: binding(root, `${output}/verification.json`), exit_code: result.status};
|
|
35
77
|
}
|
|
36
78
|
|
|
37
79
|
export function validateResearchCompletion(state, {root, continuing = false}) {
|
|
38
80
|
ensure(state?.context_reconciliation?.status === 'not-applicable' && state.context_reconciliation.reason?.trim(), 'research-context-reason-missing');
|
|
39
|
-
fs.readFileSync(
|
|
81
|
+
fs.readFileSync(file(root, state.context_reconciliation.ref));
|
|
40
82
|
for (const key of ['blocking_signals', 'drift', 'violation', 'new_impacts', 'stale_candidates']) {
|
|
41
83
|
ensure(Array.isArray(state[key]) && state[key].length === 0, `research-${key}-unresolved`);
|
|
42
84
|
}
|
|
43
85
|
const record = parse(readBound(root, state.research_verification));
|
|
44
86
|
ensure(record.schema_version === 1 && record.kind === 'maintenance-research-verification', 'research-verification-invalid');
|
|
45
87
|
for (const key of ['brief', 'evidence', 'validator']) readBound(root, record.inputs?.[key]);
|
|
88
|
+
verifyCompetitiveBindings(root, record);
|
|
46
89
|
ensure(record.inputs.validator.ref === RESEARCH_VALIDATOR, 'research-validator-mismatch');
|
|
47
90
|
ensure(record.exit_code === 0 && Number.isFinite(Date.parse(record.started_at)) && Date.parse(record.completed_at) >= Date.parse(record.started_at), 'research-verification-not-passed');
|
|
48
91
|
ensure(JSON.stringify(record.command) === JSON.stringify(['node', RESEARCH_VALIDATOR, record.inputs.brief.ref, record.inputs.evidence.ref]), 'research-command-mismatch');
|
|
49
92
|
readBound(root, record.stdout); readBound(root, record.stderr);
|
|
50
|
-
for (const ref of [state.context_reconciliation.ref, state.research_verification.ref, record.inputs.brief.ref, record.inputs.evidence.ref]) {
|
|
93
|
+
for (const ref of [state.context_reconciliation.ref, state.research_verification.ref, record.inputs.brief.ref, record.inputs.evidence.ref, ...(record.inputs.competitive || []).map(x => x.ref)]) {
|
|
51
94
|
ensure(state.evidence_refs?.includes(ref), `research-evidence-unreferenced: ${ref}`);
|
|
52
95
|
}
|
|
53
96
|
const current = execute(root, record.inputs.brief.ref, record.inputs.evidence.ref);
|
|
54
97
|
ensure(!current.error && current.status === 0, 'research-current-validation-failed');
|
|
55
98
|
readBound(root, state.research_verification);
|
|
56
|
-
for(const value of
|
|
99
|
+
for(const value of inputBindings(record.inputs))readBound(root,value);
|
|
100
|
+
verifyCompetitiveBindings(root, record);
|
|
57
101
|
if (continuing) {
|
|
58
102
|
const authorization = parse(readBound(root, state.maintenance_authorization));
|
|
59
103
|
ensure(authorization.boundary === 'template-maintenance-scope' && authorization.scope?.includes('work-unit.ssot-update'), 'maintenance-authorization-required');
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
import { resolveRuntimeLocation } from './runtime-store.mjs';
|
|
6
|
+
import { readRepositoryMode } from './repository-mode.mjs';
|
|
7
|
+
|
|
8
|
+
const PREFIX = 'maintenance:';
|
|
9
|
+
const inside = (base, target) => {
|
|
10
|
+
const relative = path.relative(base, target);
|
|
11
|
+
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function resolveMaintenanceLocation({ root, home } = {}) {
|
|
15
|
+
if (readRepositoryMode(root) !== 'template-source') throw new TypeError('maintenance 引用仅适用于 template-source');
|
|
16
|
+
// Formal maintenance materials must survive the CI runner's temporary directory.
|
|
17
|
+
const selectedHome = home ?? process.env.YSS_RUNTIME_HOME ?? path.join(os.homedir(), '.yss-harness', 'runtime');
|
|
18
|
+
assertNoSymlinks(selectedHome);
|
|
19
|
+
const location = resolveRuntimeLocation({ root, home: selectedHome });
|
|
20
|
+
assertNoSymlinks(path.join(selectedHome, location.workspaceId));
|
|
21
|
+
const directory = path.join(location.directory, 'maintenance');
|
|
22
|
+
assertNoSymlinks(directory);
|
|
23
|
+
return { ...location, runtimeDirectory: location.directory, directory };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function assertNoSymlinks(target) {
|
|
27
|
+
let cursor = path.resolve(target);
|
|
28
|
+
while (true) {
|
|
29
|
+
try {
|
|
30
|
+
if (fs.lstatSync(cursor).isSymbolicLink()) throw new TypeError(`维护路径不得穿越符号链接: ${cursor}`);
|
|
31
|
+
} catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
32
|
+
const parent = path.dirname(cursor);
|
|
33
|
+
if (parent === cursor) break;
|
|
34
|
+
cursor = parent;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function relativePath(value) {
|
|
39
|
+
if (typeof value !== 'string' || !value || value.includes('\\') || value.includes('\0') || path.posix.isAbsolute(value) || /^[A-Za-z]:/.test(value) || path.posix.normalize(value) !== value || value === '.' || value === '..' || value.startsWith('../')) {
|
|
40
|
+
throw new TypeError('maintenance 引用必须使用规范的包内相对路径');
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function resolveMaintenanceOutput(value, options = {}) {
|
|
46
|
+
const location = resolveMaintenanceLocation(options);
|
|
47
|
+
if (typeof value !== 'string' || !value) throw new TypeError('维护输出路径不能为空');
|
|
48
|
+
const target = value.startsWith(PREFIX)
|
|
49
|
+
? path.join(location.directory, relativePath(value.slice(PREFIX.length)))
|
|
50
|
+
: path.resolve(value);
|
|
51
|
+
if (!inside(location.directory, target) || target === location.directory) throw new TypeError('维护输出必须位于当前工作区的仓外 maintenance 目录');
|
|
52
|
+
assertNoSymlinks(target);
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function maintenanceReference(target, options = {}) {
|
|
57
|
+
const location = resolveMaintenanceLocation(options);
|
|
58
|
+
const absolute = resolveMaintenanceOutput(target, options);
|
|
59
|
+
return PREFIX + relativePath(path.relative(location.directory, absolute).split(path.sep).join('/'));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function resolveMaintenanceReference(reference, { root, home, digest, directory = false } = {}) {
|
|
63
|
+
if (typeof reference !== 'string' || !reference.startsWith(PREFIX)) throw new TypeError('需要显式 maintenance: 引用');
|
|
64
|
+
const target = resolveMaintenanceOutput(reference, { root, home });
|
|
65
|
+
const metadata = fs.lstatSync(target);
|
|
66
|
+
if (directory ? !metadata.isDirectory() : !metadata.isFile()) throw new TypeError(`维护引用类型不符: ${reference}`);
|
|
67
|
+
if (digest !== undefined) {
|
|
68
|
+
if (directory || !/^(?:sha256:)?[a-f0-9]{64}$/.test(digest)) throw new TypeError('维护文件摘要必须为 SHA-256');
|
|
69
|
+
const actual = crypto.createHash('sha256').update(fs.readFileSync(target)).digest('hex');
|
|
70
|
+
if (actual !== digest.replace(/^sha256:/, '')) throw new TypeError(`维护证据摘要漂移: ${reference}`);
|
|
71
|
+
}
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function resolveMaintenanceBundleFile(bundleDirectory, reference) {
|
|
76
|
+
const target = path.resolve(bundleDirectory, relativePath(reference));
|
|
77
|
+
if (!inside(bundleDirectory, target)) throw new TypeError('维护包引用越界');
|
|
78
|
+
assertNoSymlinks(target);
|
|
79
|
+
if (!fs.lstatSync(target).isFile()) throw new TypeError('维护包成员必须为普通文件');
|
|
80
|
+
return target;
|
|
81
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {approvalExpectationForCheckpoint} from './approval-consumption.mjs';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import { verifyContextReconciliation } from './context-reconciliation.mjs';
|
|
3
4
|
import { decisionIO, decisionDigest, assertUserDecisionRequirement } from './user-decision.mjs';
|
|
@@ -58,7 +59,7 @@ export function assertPlanSpecEntry(state, options = {}) {
|
|
|
58
59
|
if (gate.status !== 'approved' || !basis.has(gate.approval_ref) || !basis.has(gate.subject_ref)) fail(`命中门禁未批准或未绑定依据: ${gateId}`);
|
|
59
60
|
const record = selectApprovalRecord(io.document(gate.approval_ref), gateId);
|
|
60
61
|
if (record.gate_id !== gateId || record.subject_ref !== gate.subject_ref || !gate.approval_scope?.includes(state.feature_id) || JSON.stringify([...(gate.approval_scope || [])].sort()) !== JSON.stringify([...(record.approval_scope || [])].sort())) fail(`会签资产或范围不匹配: ${gateId}`);
|
|
61
|
-
validateApprovalRecord(record, { ...options, requireApproved: true });
|
|
62
|
+
validateApprovalRecord(record, { ...options, requireApproved: true, expected: approvalExpectationForCheckpoint(gateId,{...gate,basis:[...basis.values()].filter(asset=>gate.evidence_refs?.includes(asset.ref))},options) });
|
|
62
63
|
} else if (gate.status !== 'not-applicable' || !text(gate.reason)) fail(`未命中门禁须有原因和依据: ${gateId}`);
|
|
63
64
|
}
|
|
64
65
|
// 门禁依赖不能通过将上游标成 N/A 来跳过。
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {findApprovalCheckpoint} from './approval-checkpoint-discovery.mjs';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import { spawnSync } from 'node:child_process';
|
|
3
4
|
import { parseDocument } from '../vendor/yaml.mjs';
|
|
@@ -124,7 +125,7 @@ export function checkProjectGovernance({root,base,checkpointRef,taskRef,recovery
|
|
|
124
125
|
if(status!=='resolved')issue('task-not-resolved',`任务状态 ${status||'unknown'};先核查原任务,禁止重复派发`,ref);
|
|
125
126
|
}
|
|
126
127
|
} else if(value?.gate_id&&value?.decision==='approved') {
|
|
127
|
-
run('approval','scripts/verify-approval-record',['--require-approved',ref],[ref]);
|
|
128
|
+
try { const owner=findApprovalCheckpoint(root,ref);run('approval','scripts/verify-approval-record',['--require-approved','--checkpoint',owner,ref],[ref,owner]); } catch(error) { issue('approval-context-required',error.message,ref); }
|
|
128
129
|
} else if(claimed(value)) {
|
|
129
130
|
// Claimed assets need an owning checkpoint; owner-specific validators are called through it.
|
|
130
131
|
claimFiles.add(ref);
|
|
@@ -4,6 +4,7 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import { parse } from '../vendor/yaml.mjs';
|
|
6
6
|
import { runCommand } from './command-runner.mjs';
|
|
7
|
+
import { assertNodeVersion, beginRuntimeRun } from './runtime-store.mjs';
|
|
7
8
|
|
|
8
9
|
const ensure = (ok, message) => { if (!ok) throw new TypeError(`read-only-intake: ${message}`); };
|
|
9
10
|
export const digest = value => `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
@@ -21,7 +22,10 @@ export function intakeEvidencePath(ref, { root, runDir }) {
|
|
|
21
22
|
}
|
|
22
23
|
// Stream large preserved evidence archives instead of allocating the whole file.
|
|
23
24
|
export function fileDigest(file) {
|
|
24
|
-
|
|
25
|
+
return fileDigestWithBuffer(file, Buffer.allocUnsafe(1024 * 1024));
|
|
26
|
+
}
|
|
27
|
+
function fileDigestWithBuffer(file, buffer) {
|
|
28
|
+
const hash = createHash('sha256');
|
|
25
29
|
const fd = fs.openSync(file, 'r');
|
|
26
30
|
try {
|
|
27
31
|
let size;
|
|
@@ -30,11 +34,23 @@ export function fileDigest(file) {
|
|
|
30
34
|
} finally { fs.closeSync(fd); }
|
|
31
35
|
}
|
|
32
36
|
/** Final observable repository files, including ignored and untracked files; never follow links. */
|
|
33
|
-
export function intakeSnapshot(root) {
|
|
37
|
+
export function intakeSnapshot(root, { excludeIgnoredToolState = false } = {}) {
|
|
34
38
|
const result = spawnSync('git', ['ls-files', '-z', '--cached', '--others'], { cwd: root, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
35
39
|
ensure(result.status === 0, result.stderr || '无法观察仓库');
|
|
36
|
-
const
|
|
37
|
-
|
|
40
|
+
const refs = [...new Set(result.stdout.split('\0').filter(Boolean))].sort();
|
|
41
|
+
const ignoredToolState = new Set();
|
|
42
|
+
if (excludeIgnoredToolState) {
|
|
43
|
+
const toolRefs = refs.filter(ref => /^(?:\.codegraph|\.idea)\//.test(ref));
|
|
44
|
+
if (toolRefs.length) {
|
|
45
|
+
const ignored = spawnSync('git', ['check-ignore', '-z', '--stdin'], { cwd: root, input: toolRefs.join('\0') + '\0', encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
|
|
46
|
+
ensure([0, 1].includes(ignored.status), ignored.stderr || '无法识别工具运行缓存');
|
|
47
|
+
for (const ref of ignored.stdout.split('\0').filter(Boolean)) ignoredToolState.add(ref);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Synchronous reads share scratch space only within this snapshot, never results.
|
|
51
|
+
const rows = {}, buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
52
|
+
for (const ref of refs) {
|
|
53
|
+
if (ignoredToolState.has(ref)) continue;
|
|
38
54
|
const file = path.join(root, ref);
|
|
39
55
|
try {
|
|
40
56
|
const stat = fs.lstatSync(file);
|
|
@@ -43,8 +59,8 @@ export function intakeSnapshot(root) {
|
|
|
43
59
|
ensure(nested.status === 0 && fs.realpathSync(nested.stdout.trim()) === fs.realpathSync(file), `子仓未初始化: ${ref}`);
|
|
44
60
|
const head = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: file, encoding: 'utf8' });
|
|
45
61
|
ensure(nested.status === 0 && head.status === 0, `子仓不可观测: ${ref}`);
|
|
46
|
-
rows[ref] = { kind: 'gitlink', head: head.stdout.trim(), files: intakeSnapshot(file) };
|
|
47
|
-
} else rows[ref] = { kind: stat.isSymbolicLink() ? 'link' : 'file', mode: stat.mode, digest: stat.isSymbolicLink() ? digest(fs.readlinkSync(file)) :
|
|
62
|
+
rows[ref] = { kind: 'gitlink', head: head.stdout.trim(), files: intakeSnapshot(file, { excludeIgnoredToolState }) };
|
|
63
|
+
} else rows[ref] = { kind: stat.isSymbolicLink() ? 'link' : 'file', mode: stat.mode, digest: stat.isSymbolicLink() ? digest(fs.readlinkSync(file)) : fileDigestWithBuffer(file, buffer) };
|
|
48
64
|
} catch (error) { if (error.code === 'ENOENT') rows[ref] = null; else throw error; }
|
|
49
65
|
}
|
|
50
66
|
const head=spawnSync('git',['rev-parse','--verify','HEAD'],{cwd:root,encoding:'utf8'});
|
|
@@ -98,22 +114,29 @@ export function validateReadOnlyIntake(value, { root, runDir, roles, lifecycle }
|
|
|
98
114
|
return value;
|
|
99
115
|
}
|
|
100
116
|
/** Runner-owned evidence is outside the repository; the child receives no new repository permissions. */
|
|
101
|
-
export async function observeReadOnlyIntake(task, { root, runDir, command, args = [], timeoutMs = 0 }) {
|
|
117
|
+
export async function observeReadOnlyIntake(task, { root, runDir, command, args = [], timeoutMs = 0, runtimeStore = 'off' }) {
|
|
118
|
+
assertNodeVersion();
|
|
119
|
+
ensure(['sqlite','off'].includes(runtimeStore), 'runtime-store 必须为 sqlite 或 off');
|
|
102
120
|
const label = command ? JSON.stringify([command,...args]) : null;
|
|
103
121
|
ensure(!command || !task.verification_commands.length || task.verification_commands.length===1 && task.verification_commands[0]===label,'执行命令与声明不一致,不得替换要求的验证');
|
|
104
122
|
root = fs.realpathSync(root);
|
|
105
123
|
ensure(path.isAbsolute(runDir) && !inside(root, path.resolve(runDir)), '运行目录必须位于仓库外');
|
|
106
124
|
ensure(!inside(root, fs.realpathSync(path.dirname(runDir))), '运行目录父路径符号链接进入仓库');
|
|
125
|
+
ensure(!fs.existsSync(runDir), '运行目录必须为新目录');
|
|
126
|
+
const runtimeSession=beginRuntimeRun({root,kind:'read-only-intake',mode:runtimeStore,input:task,reportDir:runDir});
|
|
127
|
+
const storageErrors=[];
|
|
128
|
+
try {
|
|
107
129
|
fs.mkdirSync(runDir, { recursive: false });
|
|
108
130
|
runDir = fs.realpathSync(runDir);
|
|
109
131
|
fs.writeFileSync(path.join(runDir,'task.json'),JSON.stringify(task,null,2)+'\n',{flag:'wx'});
|
|
110
132
|
const before = intakeSnapshot(root), started = new Date().toISOString();
|
|
111
|
-
const result = command ? await runCommand(command, args, { cwd: root, timeoutMs, stdoutFile: path.join(runDir, 'stdout.log'), stderrFile: path.join(runDir, 'stderr.log') }) : null;
|
|
133
|
+
const result = command ? await runCommand(command, args, { cwd: root, timeoutMs, stdoutFile: path.join(runDir, 'stdout.log'), stderrFile: path.join(runDir, 'stderr.log'), runtimeSession }) : null;
|
|
134
|
+
if(result?.storageError)storageErrors.push(result.storageError);
|
|
112
135
|
const after = intakeSnapshot(root);
|
|
113
136
|
const observation = { schema_version: 1, kind: 'read-only-intake-observation', task_id: task.task_id, task_ref:'run:task.json', task_digest:digest(fs.readFileSync(path.join(runDir,'task.json'))), root, started_at: started, before, after };
|
|
114
137
|
fs.writeFileSync(path.join(runDir, 'observation.json'), JSON.stringify(observation, null, 2)+'\n');
|
|
115
138
|
const changed = JSON.stringify(before) !== JSON.stringify(after);
|
|
116
|
-
|
|
139
|
+
let completed = !changed && !storageErrors.length && (!result || result.status === 0);
|
|
117
140
|
const output = structuredClone(task);
|
|
118
141
|
output.observation_ref = 'run:observation.json';
|
|
119
142
|
output.verification_status = result ? 'executed' : 'not-executed';
|
|
@@ -128,5 +151,17 @@ export async function observeReadOnlyIntake(task, { root, runDir, command, args
|
|
|
128
151
|
output.workflow_status = completed ? 'resolved' : 'failed';
|
|
129
152
|
output.result = { result_schema: 'workflow-execution-result-v1', work_unit: task.work_unit_id, workflow_reference: task.contract.contract_ref, result: completed ? 'completed' : 'failed', skill: 'yss-research', changed_files: [], changed_artifacts: [], evidence_refs: [...task.inputs, 'run:observation.json'], context_reconciliation: {status:'not-applicable',ref:'CONTEXT.md',reason:'只读分诊,不批准资产或流转阶段'}, deferred_seams: [], drift: [], violation: changed ? ['repository-changed'] : [], new_impacts: [], stale_candidates: [], next_route: null, blocking_signals: completed ? [] : ['read-only-intake-failed'] };
|
|
130
153
|
fs.writeFileSync(path.join(runDir, 'task-result.json'), JSON.stringify(output, null, 2)+'\n');
|
|
154
|
+
if(runtimeSession)try {
|
|
155
|
+
runtimeSession.recordEvent('repository-observation',{changed,started_at:started});
|
|
156
|
+
runtimeSession.registerFiles(runDir);
|
|
157
|
+
runtimeSession.finish({status:completed?'completed':'failed',exitCode:result?.status??0,report:path.join(runDir,'task-result.json')});
|
|
158
|
+
runtimeSession.pin('read-only-intake-evidence');
|
|
159
|
+
}catch(error){storageErrors.push(error.message);}
|
|
160
|
+
if(storageErrors.length){
|
|
161
|
+
output.workflow_status='failed';output.result.result='failed';output.result.blocking_signals.push('runtime-store-failed');
|
|
162
|
+
fs.writeFileSync(path.join(runDir, 'task-result.json'), JSON.stringify(output, null, 2)+'\n');
|
|
163
|
+
process.stderr.write(`运行存储异常,实际执行结果及日志已保留: ${storageErrors.join('; ')}\n`);
|
|
164
|
+
}
|
|
131
165
|
return output;
|
|
166
|
+
} finally { runtimeSession?.close(); }
|
|
132
167
|
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { readFileSync, existsSync, lstatSync, realpathSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { parseDocument } from '../vendor/yaml.mjs';
|
|
5
|
+
import { loadRegistry, ROOT } from './lifecycle-registry.mjs';
|
|
6
|
+
import { validateTaskPackageSchema } from './task-package-schema.mjs';
|
|
7
|
+
import { loadSkillRegistry } from './skill-registry.mjs';
|
|
8
|
+
|
|
9
|
+
export const REVIEW_POLICY_REF = '.template-spec/agents/digital-human-roles.yaml';
|
|
10
|
+
const HEX = /^[a-f0-9]{64}$/;
|
|
11
|
+
const ID = /^capability\.[a-z0-9][a-z0-9-]*$/;
|
|
12
|
+
const REVIEW_STATES = new Set(['Reviewer', 'Verifier']);
|
|
13
|
+
const contextFields = ['check_ids', 'capability_ids', 'candidate_ref', 'candidate_digest', 'policy_ref', 'policy_digest', 'reviewer_principal_ref', 'drafter_principal_ref', 'approval_scope', 'basis', 'current_approvals'];
|
|
14
|
+
const fail = (code, detail) => { const error = new TypeError(`${code}: ${detail}`); error.code = code; throw error; };
|
|
15
|
+
const strings = value => Array.isArray(value) && value.length > 0 && value.every(x => typeof x === 'string' && x.trim()) && new Set(value).size === value.length;
|
|
16
|
+
const same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
17
|
+
const sameSet = (a, b) => strings(a) && strings(b) && same([...a].sort(), [...b].sort());
|
|
18
|
+
const canonical = value => Array.isArray(value) ? value.map(canonical) : value && typeof value === 'object' ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])])) : value;
|
|
19
|
+
export const reviewDigest = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
20
|
+
export const currentApprovalsDigest = rows => reviewDigest(JSON.stringify(canonical(rows)));
|
|
21
|
+
|
|
22
|
+
export function reviewLocalPath(root, ref, { missing = false } = {}) {
|
|
23
|
+
if (typeof ref !== 'string' || !ref.trim() || path.isAbsolute(ref) || ref.includes('\\') || ref.split('/').includes('..') || /^[a-z][a-z0-9+.-]*:/i.test(ref)) fail('APPROVAL_CONTEXT_REQUIRED', `必须使用项目内相对引用: ${ref}`);
|
|
24
|
+
const file = path.resolve(root, ref), rel = path.relative(root, file);
|
|
25
|
+
if (!rel || rel === '..' || rel.startsWith(`..${path.sep}`)) fail('APPROVAL_CONTEXT_REQUIRED', `引用越界: ${ref}`);
|
|
26
|
+
let current = path.resolve(root);
|
|
27
|
+
for (const segment of rel.split(path.sep)) {
|
|
28
|
+
current = path.join(current, segment);
|
|
29
|
+
if (existsSync(current) && lstatSync(current).isSymbolicLink()) fail('APPROVAL_CONTEXT_REQUIRED', `引用不得经过 symlink: ${ref}`);
|
|
30
|
+
}
|
|
31
|
+
if (existsSync(file) && existsSync(root)) {
|
|
32
|
+
const realRelative = path.relative(realpathSync(root), realpathSync(file));
|
|
33
|
+
if (realRelative === '..' || realRelative.startsWith(`..${path.sep}`)) fail('APPROVAL_CONTEXT_REQUIRED', `引用越界: ${ref}`);
|
|
34
|
+
} else if (!missing && !existsSync(root)) fail('APPROVAL_CONTEXT_REQUIRED', '项目根不可读');
|
|
35
|
+
return file;
|
|
36
|
+
}
|
|
37
|
+
function io({ root = ROOT, read = file => readFileSync(file) } = {}) {
|
|
38
|
+
const bytes = ref => { try { return Buffer.from(read(reviewLocalPath(root, ref))); } catch (error) { if (error.code === 'APPROVAL_CONTEXT_REQUIRED') throw error; fail('APPROVAL_CONTEXT_REQUIRED', `当前引用不可读: ${ref}`); } };
|
|
39
|
+
const document = ref => {
|
|
40
|
+
const doc = parseDocument(String(bytes(ref)), { uniqueKeys: true, maxAliasCount: 0 });
|
|
41
|
+
if (doc.errors.length) fail('APPROVAL_CONTEXT_REQUIRED', `当前引用格式无效: ${ref}`);
|
|
42
|
+
return doc.toJS({ maxAliasCount: 0 });
|
|
43
|
+
};
|
|
44
|
+
return { root, read, bytes, document };
|
|
45
|
+
}
|
|
46
|
+
export function reviewAuthorityRegistries(options = {}) {
|
|
47
|
+
const source=io(options);
|
|
48
|
+
const current=(key,ref,fallback)=>{
|
|
49
|
+
if(!existsSync(reviewLocalPath(source.root,ref,{missing:true})))return options[key] || fallback();
|
|
50
|
+
const actual=source.document(ref);
|
|
51
|
+
if(options[key] && !same(canonical(options[key]),canonical(actual)))fail('REVIEW_BINDING_STALE',`传入注册表与目标项目事实源不一致: ${ref}`);
|
|
52
|
+
return actual;
|
|
53
|
+
};
|
|
54
|
+
return {registry:current('registry','.template-spec/process/lifecycle-registry.yaml',loadRegistry),skillRegistry:current('skillRegistry','.template-spec/agents/yss-skill-registry.yaml',loadSkillRegistry)};
|
|
55
|
+
}
|
|
56
|
+
export function reviewRuleForBoundary(rolesDoc, boundary) {
|
|
57
|
+
const policy = rolesDoc.gate_policy || {};
|
|
58
|
+
const rule = [...(policy.check_reviews || []), ...(policy.digital_human_review || []), ...(policy.dual_digital_human || [])].find(x => x.gate === boundary);
|
|
59
|
+
if (!rule) fail('GATE_POLICY_REQUIRED', `没有专业审查能力政策: ${boundary}`);
|
|
60
|
+
return rule;
|
|
61
|
+
}
|
|
62
|
+
export function validateReviewCapabilityPolicy(rolesDoc, { skillIds, gateIds, checkIds } = {}) {
|
|
63
|
+
if (rolesDoc.gate_policy?.default_if_unlisted !== 'reject-unlisted') fail('GATE_POLICY_REQUIRED', '当前能力政策必须拒绝未分类边界');
|
|
64
|
+
if (!Array.isArray(rolesDoc.review_capabilities) || !rolesDoc.review_capabilities.length) fail('GATE_POLICY_REQUIRED', '缺少 review_capabilities');
|
|
65
|
+
const roles = new Map((rolesDoc.roles || []).map(role => [role.id, role]));
|
|
66
|
+
const ids = new Set();
|
|
67
|
+
for (const item of rolesDoc.review_capabilities) {
|
|
68
|
+
if (!ID.test(item?.id || '') || ids.has(item.id)) fail('GATE_POLICY_REQUIRED', `能力编号未知或重复: ${item?.id}`);
|
|
69
|
+
ids.add(item.id);
|
|
70
|
+
if (typeof item.name !== 'string' || !item.name.trim() || !strings(item.eligible_roles) || !strings(item.review_skills)) fail('GATE_POLICY_REQUIRED', `${item.id} 缺少能力名称、角色或技能`);
|
|
71
|
+
for (const skill of item.review_skills) if (skillIds && !skillIds.has(skill)) fail('GATE_POLICY_REQUIRED', `未登记审查技能: ${skill}`);
|
|
72
|
+
for (const roleId of item.eligible_roles) {
|
|
73
|
+
const role = roles.get(roleId);
|
|
74
|
+
if (!role) fail('GATE_POLICY_REQUIRED', `能力引用未知角色: ${roleId}`);
|
|
75
|
+
if (item.review_skills.some(skill => role.forbidden_skills.includes(skill))) fail('REVIEW_CAPABILITY_FORBIDDEN', `${roleId} 的禁止技能不能成为审查补充能力`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const rules = [...(rolesDoc.gate_policy?.check_reviews || []), ...(rolesDoc.gate_policy?.digital_human_review || []), ...(rolesDoc.gate_policy?.dual_digital_human || []), ...(rolesDoc.gate_policy?.digital_human_review_work_units || [])];
|
|
79
|
+
for (const rule of rules) {
|
|
80
|
+
if (!strings(rule.capability_ids) || rule.capability_ids.some(id => !ids.has(id))) fail('GATE_POLICY_REQUIRED', `${rule.gate || rule.work_unit} 缺少完整能力分类`);
|
|
81
|
+
}
|
|
82
|
+
if (gateIds || checkIds) {
|
|
83
|
+
const policy = rolesDoc.gate_policy;
|
|
84
|
+
const classifiedGates = [...(policy.evidence_only || []), ...(policy.orchestrator || []), ...(policy.product_digital_human_with_biological_veto || []), ...(policy.biological_human || []), ...(policy.digital_human_review || []).map(x => x.gate), ...(policy.dual_digital_human || []).map(x => x.gate)];
|
|
85
|
+
const classifiedChecks = [...(policy.automatic_checks || []), ...(policy.check_reviews || []).map(x => x.gate)];
|
|
86
|
+
for (const [expected, actual, kind] of [[gateIds, classifiedGates, 'gate'], [checkIds, classifiedChecks, 'check']]) {
|
|
87
|
+
if (!expected) continue;
|
|
88
|
+
if (new Set(actual).size !== actual.length || actual.some(id => !expected.has(id)) || [...expected].some(id => !actual.includes(id))) fail('GATE_POLICY_REQUIRED', `${kind} 分类必须完整且唯一`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { capability_count: ids.size };
|
|
92
|
+
}
|
|
93
|
+
export function compileReviewCapabilities({ checkIds, roleId, executionState = 'Reviewer', rolesDoc, registry = loadRegistry(), skillRegistry = loadSkillRegistry() }) {
|
|
94
|
+
if (!rolesDoc) fail('GATE_POLICY_REQUIRED', '缺少权威能力政策');
|
|
95
|
+
const skillIds = new Set(['skills', 'platform_skills', 'external_skills'].flatMap(key => (skillRegistry[key] || []).map(item => item.id)));
|
|
96
|
+
validateReviewCapabilityPolicy(rolesDoc, { skillIds, gateIds: new Set(registry.gates.map(x => x.id)), checkIds: new Set((registry.checks || []).map(x => x.id)) });
|
|
97
|
+
if (!REVIEW_STATES.has(executionState)) fail('REVIEW_CAPABILITY_STATE', '审查补充技能只允许 Reviewer / Verifier');
|
|
98
|
+
if (!strings(checkIds)) fail('GATE_POLICY_REQUIRED', 'check_ids 必须是非空唯一数组');
|
|
99
|
+
const role = rolesDoc.roles.find(x => x.id === roleId);
|
|
100
|
+
if (!role) fail('REVIEW_CAPABILITY_MISSING', `未知审查角色: ${roleId}`);
|
|
101
|
+
const known = new Map([...registry.gates, ...(registry.checks || [])].map(x => [x.id, x]));
|
|
102
|
+
const capabilityIds = [...new Set(checkIds.flatMap(id => {
|
|
103
|
+
if (!known.has(id)) fail('GATE_POLICY_REQUIRED', `未知当前审查边界: ${id}`);
|
|
104
|
+
const rule = reviewRuleForBoundary(rolesDoc, id);
|
|
105
|
+
if (!strings(rule.capability_ids)) fail('GATE_POLICY_REQUIRED', `${id} 缺少能力要求`);
|
|
106
|
+
return rule.capability_ids;
|
|
107
|
+
}))].sort();
|
|
108
|
+
const skills = new Set(), stages = new Set(checkIds.map(id => known.get(id).stage));
|
|
109
|
+
for (const id of capabilityIds) {
|
|
110
|
+
const capability = rolesDoc.review_capabilities?.find(x => x.id === id);
|
|
111
|
+
if (!capability?.eligible_roles.includes(roleId)) fail('REVIEW_CAPABILITY_MISSING', `${roleId} 缺少 ${id};应增加有该能力的专家`);
|
|
112
|
+
for (const skill of capability.review_skills) {
|
|
113
|
+
if (role.forbidden_skills.includes(skill)) fail('REVIEW_CAPABILITY_FORBIDDEN', `${roleId} 禁止加载 ${skill}`);
|
|
114
|
+
skills.add(skill);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { check_ids: [...checkIds], capability_ids: capabilityIds, review_skills: [...skills].sort(), review_stages: [...stages] };
|
|
118
|
+
}
|
|
119
|
+
/** Supplements existing professional work-unit reviews; it grants no approval boundary. */
|
|
120
|
+
export function compileWorkUnitReviewCapabilities({workUnitId,roleId,executionState='Reviewer',rolesDoc,registry=loadRegistry()}) {
|
|
121
|
+
if(!REVIEW_STATES.has(executionState)) fail('REVIEW_CAPABILITY_STATE','工作单元审查能力只允许 Reviewer / Verifier');
|
|
122
|
+
const skillRegistry=loadSkillRegistry();
|
|
123
|
+
validateReviewCapabilityPolicy(rolesDoc,{skillIds:new Set(['skills','platform_skills','external_skills'].flatMap(key=>(skillRegistry[key] || []).map(row=>row.id))),gateIds:new Set(registry.gates.map(row=>row.id)),checkIds:new Set((registry.checks || []).map(row=>row.id))});
|
|
124
|
+
const unit=registry.work_units.find(row=>row.id===workUnitId), rule=rolesDoc.gate_policy.digital_human_review_work_units?.find(row=>row.work_unit===workUnitId), role=rolesDoc.roles.find(row=>row.id===roleId);
|
|
125
|
+
if(!unit || !rule || !role) fail('GATE_POLICY_REQUIRED','工作单元没有已登记的专业审查政策');
|
|
126
|
+
const skills=new Set();
|
|
127
|
+
for(const id of rule.capability_ids) {
|
|
128
|
+
const capability=rolesDoc.review_capabilities.find(row=>row.id===id);
|
|
129
|
+
if(!capability?.eligible_roles.includes(roleId)) fail('REVIEW_CAPABILITY_MISSING',`${roleId} 缺少 ${id};应增加专家`);
|
|
130
|
+
for(const skill of capability.review_skills) {if(role.forbidden_skills.includes(skill)) fail('REVIEW_CAPABILITY_FORBIDDEN',`${roleId} 禁止加载 ${skill}`);skills.add(skill);}
|
|
131
|
+
}
|
|
132
|
+
return {capability_ids:[...rule.capability_ids].sort(),review_skills:[...skills].sort(),review_stages:[unit.stage]};
|
|
133
|
+
}
|
|
134
|
+
/** Read current subjects from consumer-owned checkpoint rows, never from approvals. */
|
|
135
|
+
export function currentApprovalsFromCheckpoint(checkpointRef, boundaries, options = {}) {
|
|
136
|
+
const source = io(options), checkpoint = source.document(checkpointRef);
|
|
137
|
+
if (!strings(boundaries)) fail('APPROVAL_CONTEXT_REQUIRED', '缺少当前检查范围');
|
|
138
|
+
return boundaries.map(boundary => {
|
|
139
|
+
const row = checkpoint[boundary.startsWith('check.') ? 'checks' : 'gates']?.[boundary];
|
|
140
|
+
if (!row?.subject_ref || !strings(row.approval_scope) || !row.drafter_principal_ref?.trim() || !Array.isArray(row.basis) || !row.basis.length || new Set(row.basis.map(x => x.ref)).size !== row.basis.length) fail('APPROVAL_CONTEXT_REQUIRED', `${boundary} 缺少当前 subject、scope、basis 或作者来源`);
|
|
141
|
+
const subjectDigest = reviewDigest(source.bytes(row.subject_ref));
|
|
142
|
+
if (row.subject_digest !== undefined && row.subject_digest !== subjectDigest) fail('REVIEW_BINDING_STALE', `${boundary} 当前 subject 摘要漂移`);
|
|
143
|
+
for (const item of row.basis) if (!/^(sha256:)?[a-f0-9]{64}$/.test(item.digest || '') || reviewDigest(source.bytes(item.ref)) !== item.digest.replace(/^sha256:/,'')) fail('REVIEW_BINDING_STALE', `${boundary} 当前 basis 摘要漂移`);
|
|
144
|
+
for (const evidence of row.evidence_refs || []) if (!row.basis.some(x => x.ref === evidence)) fail('APPROVAL_CONTEXT_REQUIRED', `${boundary} 证据必须包含在当前 basis`);
|
|
145
|
+
const basis=row.basis.filter(item=>![row.subject_ref,row.approval_ref].includes(item.ref)).map(item=>({ref:item.ref,digest:item.digest.replace(/^sha256:/,'')}));
|
|
146
|
+
if(!basis.length) fail('APPROVAL_CONTEXT_REQUIRED', `${boundary} 缺少独立审查依据`);
|
|
147
|
+
return { boundary, subject_ref: row.subject_ref, subject_digest: subjectDigest, approval_scope: [...row.approval_scope], basis, drafter_principal_ref: row.drafter_principal_ref };
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function currentContext(context, source) {
|
|
151
|
+
if (!context || contextFields.some(field => context[field] === undefined)) fail('APPROVAL_CONTEXT_REQUIRED', '缺少完整当前 review_context');
|
|
152
|
+
for (const field of ['check_ids', 'capability_ids', 'approval_scope']) if (!strings(context[field])) fail('APPROVAL_CONTEXT_REQUIRED', `${field} 无效`);
|
|
153
|
+
if (context.reviewer_principal_ref === context.drafter_principal_ref || !context.reviewer_principal_ref?.trim() || !context.drafter_principal_ref?.trim()) fail('REVIEW_NOT_INDEPENDENT', '审查实例必须不同于起草实例');
|
|
154
|
+
for (const [ref, digest] of [[context.candidate_ref, context.candidate_digest], [context.policy_ref, context.policy_digest]]) {
|
|
155
|
+
if (!HEX.test(digest || '') || reviewDigest(source.bytes(ref)) !== digest) fail('REVIEW_BINDING_STALE', `当前候选或政策摘要过期: ${ref}`);
|
|
156
|
+
}
|
|
157
|
+
if (context.policy_ref !== REVIEW_POLICY_REF) fail('GATE_POLICY_REQUIRED', '政策引用必须指向唯一角色事实源');
|
|
158
|
+
if (!Array.isArray(context.basis) || !context.basis.length || new Set(context.basis.map(x => x.ref)).size !== context.basis.length) fail('APPROVAL_CONTEXT_REQUIRED', '缺少唯一当前 basis');
|
|
159
|
+
for (const item of context.basis) if (!HEX.test(item.digest || '') || reviewDigest(source.bytes(item.ref)) !== item.digest) fail('REVIEW_BINDING_STALE', `当前依据摘要过期: ${item.ref}`);
|
|
160
|
+
const rows = context.current_approvals;
|
|
161
|
+
if (!Array.isArray(rows) || !sameSet(rows.map(row => row.boundary), context.check_ids)) fail('APPROVAL_CONTEXT_REQUIRED', 'current_approvals 必须完整且唯一覆盖 check_ids');
|
|
162
|
+
for (const row of rows) {
|
|
163
|
+
if (!HEX.test(row.subject_digest || '') || reviewDigest(source.bytes(row.subject_ref)) !== row.subject_digest) fail('REVIEW_BINDING_STALE', `当前逐项主体已变化: ${row.subject_ref}`);
|
|
164
|
+
if (!strings(row.approval_scope) || row.approval_scope.some(x => !context.approval_scope.includes(x)) || !row.drafter_principal_ref?.trim() || row.drafter_principal_ref === context.reviewer_principal_ref) fail('REVIEW_NOT_INDEPENDENT', '逐项范围或独立作者来源无效');
|
|
165
|
+
if (!Array.isArray(row.basis) || !row.basis.length || new Set(row.basis.map(x => x.ref)).size !== row.basis.length || row.basis.some(item => !context.basis.some(bound => same(canonical(bound), canonical(item))))) fail('APPROVAL_CONTEXT_REQUIRED', '逐项 basis 必须绑定当前任务依据');
|
|
166
|
+
}
|
|
167
|
+
const candidate = source.document(context.candidate_ref);
|
|
168
|
+
if (candidate?.schema_version !== 1 || candidate.kind !== 'review-subject' || !same(canonical(candidate.current_approvals), canonical(rows))) fail('REVIEW_BINDING_STALE', '候选审阅包未绑定逐项当前上下文');
|
|
169
|
+
if (!candidate.source_checkpoint?.ref || !HEX.test(candidate.source_checkpoint.digest || '')) fail('REVIEW_BINDING_STALE', '当前审阅包缺少 checkpoint 来源');
|
|
170
|
+
const currentRows = currentApprovalsFromCheckpoint(candidate.source_checkpoint.ref, context.check_ids, source);
|
|
171
|
+
const checkpointDigest = candidate.source_checkpoint.binding_kind === 'current-approvals-v1' ? currentApprovalsDigest(currentRows) : reviewDigest(source.bytes(candidate.source_checkpoint.ref));
|
|
172
|
+
if(checkpointDigest !== candidate.source_checkpoint.digest) fail('REVIEW_BINDING_STALE','当前审阅包的 checkpoint 批准输入已变化');
|
|
173
|
+
if (!same(canonical(currentRows), canonical(rows))) fail('REVIEW_BINDING_STALE', '逐项预期与当前 checkpoint 消费上下文不同');
|
|
174
|
+
|
|
175
|
+
}
|
|
176
|
+
export function validateReviewTaskBinding(task, options = {}) {
|
|
177
|
+
const source = io(options);
|
|
178
|
+
validateTaskPackageSchema(task);
|
|
179
|
+
if (task.schema_version !== 1 || !REVIEW_STATES.has(task.execution_state) || !['active', 'resolved'].includes(task.workflow_status) || task.contract.status !== 'issued') fail('REVIEW_CAPABILITY_STATE', '只接受 issued 的正式 v1 Reviewer / Verifier 任务');
|
|
180
|
+
currentContext(task.review_context, source);
|
|
181
|
+
const policy = source.document(task.review_context.policy_ref), rolesDoc = options.rolesDoc || policy;
|
|
182
|
+
if (!same(canonical(rolesDoc), canonical(policy))) fail('REVIEW_BINDING_STALE', '传入角色政策与当前政策字节不一致');
|
|
183
|
+
const role = rolesDoc.roles.find(x => x.id === task.role_id);
|
|
184
|
+
if (!role || !rolesDoc.runtimes.some(x => x.id === task.runtime_id)) fail('REVIEW_CAPABILITY_MISSING', '未登记审查角色或运行时');
|
|
185
|
+
if (task.skill_source.registry_ref !== REVIEW_POLICY_REF || task.skill_source.defaults_ref !== `taskPackageDefaults(${task.role_id})` || !same(task.skill_source.core_skills, role.core_skills) || !same(task.skill_source.forbidden_skills, role.forbidden_skills)) fail('REVIEW_CAPABILITY_FORBIDDEN', 'core / forbidden 技能不得通过能力任务改变');
|
|
186
|
+
const authority=reviewAuthorityRegistries(options);
|
|
187
|
+
const compiled = compileReviewCapabilities({ checkIds: task.review_context.check_ids, roleId: task.role_id, executionState: task.execution_state, rolesDoc, ...authority });
|
|
188
|
+
const unit=authority.registry.work_units.find(row=>row.id===task.work_unit_id),checkpointRef=source.document(task.review_context.candidate_ref).source_checkpoint.ref;
|
|
189
|
+
if(!unit || unit.scope==='template-source' || task.work_unit_id==='work-unit.slice-implementation' || !task.stage_id || !authority.registry.stages.some(row=>row.id===task.stage_id) || (unit.stage && task.stage_id!==unit.stage))fail('REVIEW_TASK_INVALID','正式生命周期审查任务须绑定已登记工作单元及阶段;切片实现必须使用 slice-implementation 合同');
|
|
190
|
+
if(task.contract.kind!=='lifecycle-work-unit' || task.contract.slice_contract_ref || task.contract.maintenance_ref || task.contract.contract_ref!==task.review_context.candidate_ref || task.contract.lifecycle_ref!==checkpointRef || task.checkpoint_ref!==checkpointRef || task.convergence.parent_work_unit!==task.work_unit_id || task.convergence.convergence_ref!==checkpointRef)fail('REVIEW_TASK_INVALID','正式审查任务的合同、checkpoint 与汇合来源不一致');
|
|
191
|
+
for(const ref of [checkpointRef,task.review_context.policy_ref,task.review_context.candidate_ref,...task.review_context.basis.map(row=>row.ref),...task.review_context.current_approvals.map(row=>row.subject_ref)])if(!task.inputs.includes(ref))fail('REVIEW_TASK_INVALID','正式审查任务输入未覆盖当前审查来源: '+ref);
|
|
192
|
+
if (!sameSet(task.review_context.capability_ids, compiled.capability_ids) || !same(task.skill_source.review_skills, compiled.review_skills)) fail('REVIEW_CAPABILITY_MISSING', 'capability_ids / review_skills 必须由当前能力政策编译');
|
|
193
|
+
if (task.stage_id && !compiled.review_stages.includes(task.stage_id)) fail('REVIEW_CAPABILITY_MISSING', '审查阶段与当前检查不匹配');
|
|
194
|
+
if (task.review_context.implementation_actor_id === task.actor_id) fail('REVIEW_NOT_INDEPENDENT', '审查 actor 不得等于实现 actor');
|
|
195
|
+
const protectedRefs = [task.review_context.candidate_ref, task.review_context.policy_ref, ...task.review_context.basis.map(x => x.ref), ...task.review_context.current_approvals.map(x => x.subject_ref), source.document(task.review_context.candidate_ref).source_checkpoint.ref];
|
|
196
|
+
for (const allowed of task.allowed_write_paths) {
|
|
197
|
+
const output = reviewLocalPath(source.root, allowed, { missing: true });
|
|
198
|
+
for (const ref of protectedRefs) {
|
|
199
|
+
const rel = path.relative(output, reviewLocalPath(source.root, ref));
|
|
200
|
+
if (!rel || (rel !== '..' && !rel.startsWith(`..${path.sep}`))) fail('REVIEW_CAPABILITY_WRITE_SCOPE', '审查输出范围不得覆盖候选、政策或依据');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (options.expected) {
|
|
204
|
+
for (const field of contextFields) {
|
|
205
|
+
const equal = ['check_ids', 'capability_ids', 'approval_scope'].includes(field) ? sameSet(task.review_context[field], options.expected[field]) : same(canonical(task.review_context[field]), canonical(options.expected[field]));
|
|
206
|
+
if (!equal) fail('REVIEW_BINDING_STALE', `任务未绑定当前消费者 ${field}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return { ...task.review_context, ...compiled };
|
|
210
|
+
}
|
|
211
|
+
export function approvalExpectedFromTask(task, { boundary, reviewTaskRef, reviewTaskDigest, ...options } = {}) {
|
|
212
|
+
const context = validateReviewTaskBinding(task, options);
|
|
213
|
+
const rows = task.review_context?.current_approvals;
|
|
214
|
+
const matches = rows?.filter(row => row.boundary === boundary);
|
|
215
|
+
if (!context.check_ids.includes(boundary) || matches?.length !== 1) fail('APPROVAL_CONTEXT_REQUIRED', '任务缺少当前边界的独立 subject / scope / basis 来源');
|
|
216
|
+
const row = matches[0], source = io(options);
|
|
217
|
+
if (!HEX.test(row.subject_digest || '') || reviewDigest(source.bytes(row.subject_ref)) !== row.subject_digest) fail('REVIEW_BINDING_STALE', '任务的当前审查主体摘要过期');
|
|
218
|
+
if (!strings(row.approval_scope) || row.approval_scope.some(x => !context.approval_scope.includes(x)) || !row.drafter_principal_ref?.trim() || row.drafter_principal_ref === context.reviewer_principal_ref || !Array.isArray(row.basis) || !row.basis.length) fail('APPROVAL_CONTEXT_REQUIRED', '任务的当前审查行缺少范围、依据或作者绑定');
|
|
219
|
+
for (const basis of row.basis) if (!context.basis.some(x => same(canonical(x), canonical(basis)))) fail('REVIEW_BINDING_STALE', '行依据不属于当前任务 basis');
|
|
220
|
+
if (!reviewTaskRef || !HEX.test(reviewTaskDigest || '') || reviewDigest(source.bytes(reviewTaskRef)) !== reviewTaskDigest) fail('APPROVAL_CONTEXT_REQUIRED', '缺少当前任务文件及摘要');
|
|
221
|
+
if (!same(canonical(task), canonical(source.document(reviewTaskRef)))) fail('REVIEW_BINDING_STALE', '传入任务不是所绑定任务文件的当前字节');
|
|
222
|
+
return { ...row, review_context: { ...task.review_context, review_task_ref: reviewTaskRef, review_task_digest: reviewTaskDigest } };
|
|
223
|
+
}
|
|
224
|
+
export function assertReviewCapabilityBinding(record, { expected, ...options } = {}) {
|
|
225
|
+
if (!expected?.review_task_ref || !HEX.test(expected.review_task_digest || '')) fail('APPROVAL_CONTEXT_REQUIRED', '缺少消费者当前 review task 来源');
|
|
226
|
+
if (record.review_task_ref !== expected.review_task_ref || record.review_task_digest !== expected.review_task_digest) fail('REVIEW_BINDING_STALE', '会签未绑定当前审查任务');
|
|
227
|
+
const source = io(options), bytes = source.bytes(expected.review_task_ref);
|
|
228
|
+
if (reviewDigest(bytes) !== expected.review_task_digest) fail('REVIEW_BINDING_STALE', '审查任务字节已变化');
|
|
229
|
+
const task = source.document(expected.review_task_ref);
|
|
230
|
+
const context = validateReviewTaskBinding(task, { ...options, expected });
|
|
231
|
+
if (!context.check_ids.includes(record.gate_id) || !sameSet(record.capability_ids, context.capability_ids)) fail('REVIEW_CAPABILITY_MISSING', '会签边界或能力不属于当前任务');
|
|
232
|
+
if (record.role_id !== task.role_id || record.runtime_id !== task.runtime_id || record.principal_ref !== context.reviewer_principal_ref || record.drafter_principal_ref !== context.current_approvals.find(row => row.boundary === record.gate_id)?.drafter_principal_ref) fail('REVIEW_NOT_INDEPENDENT', '会签与任务中的角色、运行时或独立身份不匹配');
|
|
233
|
+
return { task_id: task.task_id, capability_ids: context.capability_ids, review_skills: context.review_skills };
|
|
234
|
+
}
|