create-yss-spec 2.2.3 → 2.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/package.json +1 -1
- package/src/cli.js +15 -1
- package/src/self-update.js +277 -0
- package/template/.agents/skills/yss-domain/SKILL.md +5 -4
- package/template/.agents/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.agents/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.agents/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.agents/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.agents/skills/yss-router/SKILL.md +4 -2
- package/template/.agents/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.agents/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.agents/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.agents/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.agents/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.agents/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.agents/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.agents/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.agents/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.agents/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.agents/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.agents/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.agents/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.claude/skills/yss-domain/SKILL.md +5 -4
- package/template/.claude/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.claude/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.claude/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.claude/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.claude/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.claude/skills/yss-router/SKILL.md +4 -2
- package/template/.claude/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.claude/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.claude/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.claude/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.claude/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.claude/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.claude/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.claude/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.claude/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.claude/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.claude/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.claude/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.claude/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.claude/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.claude/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.claude/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.claude/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.claude/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.claude/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.claude/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.claude/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.codex/skills/yss-domain/SKILL.md +5 -4
- package/template/.codex/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.codex/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.codex/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.codex/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.codex/skills/yss-router/SKILL.md +4 -2
- package/template/.codex/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.codex/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.codex/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.codex/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.codex/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.codex/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.codex/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.codex/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.codex/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.codex/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.codex/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.codex/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.codex/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.cursor/skills/yss-domain/SKILL.md +5 -4
- package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.cursor/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.cursor/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.cursor/skills/yss-router/SKILL.md +4 -2
- package/template/.cursor/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.cursor/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.cursor/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.cursor/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.cursor/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.cursor/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.cursor/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.cursor/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.cursor/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.cursor/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.cursor/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.cursor/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.hermes/skills/yss-domain/SKILL.md +5 -4
- package/template/.hermes/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.hermes/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.hermes/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.hermes/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.hermes/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.hermes/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.hermes/skills/yss-router/SKILL.md +4 -2
- package/template/.hermes/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.hermes/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.hermes/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.hermes/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.hermes/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.hermes/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.hermes/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.hermes/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.hermes/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.hermes/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.hermes/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.hermes/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.hermes/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.hermes/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.hermes/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.hermes/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.hermes/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.hermes/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.hermes/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.hermes/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.pi/skills/yss-domain/SKILL.md +5 -4
- package/template/.pi/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.pi/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.pi/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.pi/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.pi/skills/yss-router/SKILL.md +4 -2
- package/template/.pi/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.pi/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.pi/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.pi/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.pi/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.pi/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.pi/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.pi/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.pi/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.pi/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.pi/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.pi/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.pi/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.qoder/skills/yss-domain/SKILL.md +5 -4
- package/template/.qoder/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.qoder/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.qoder/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.qoder/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.qoder/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.qoder/skills/yss-router/SKILL.md +4 -2
- package/template/.qoder/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.qoder/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.qoder/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.qoder/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.qoder/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.qoder/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.qoder/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.qoder/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.qoder/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.qoder/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.qoder/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.qoder/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.qoder/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.qoder/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.qoder/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.qoder/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.qoder/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.qoder/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.qoder/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.qoder/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/.trae/skills/yss-domain/SKILL.md +5 -4
- package/template/.trae/skills/yss-product-lifecycle/SKILL.md +5 -5
- package/template/.trae/skills/yss-product-lifecycle/references/artifact-dependencies.md +4 -0
- package/template/.trae/skills/yss-product-lifecycle/references/matt-yss-adapter.md +2 -2
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration-contract.yaml +139 -4
- package/template/.trae/skills/yss-product-lifecycle/references/orchestration.md +6 -3
- package/template/.trae/skills/yss-product-lifecycle/references/state-model.md +7 -2
- package/template/.trae/skills/yss-router/SKILL.md +4 -2
- package/template/.trae/skills/yss-router/references/router-contract.yaml +18 -3
- package/template/.trae/skills/yss-router/references/slice-implementation-contract.md +58 -1
- package/template/.trae/skills/yss-stage-decision/SKILL.md +50 -0
- package/template/.trae/skills/yss-stage-decision/references/domain-strategy-contract.md +26 -0
- package/template/.trae/skills/yss-stage-decision/references/domain-strategy.schema.json +128 -0
- package/template/.trae/skills/yss-stage-decision/references/stage-decision-package-contract.md +19 -0
- package/template/.trae/skills/yss-stage-decision/references/stage-decision-package.schema.json +86 -0
- package/template/.trae/skills/yss-stage-decision/references/validation-rules.md +18 -0
- package/template/.trae/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +169 -0
- package/template/.trae/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +100 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml +15 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/invalid-direction.yaml +58 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/invalid-stage-decision-blocker.yaml +44 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml +15 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +49 -0
- package/template/.trae/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +108 -0
- package/template/.trae/skills/yss-stage-decision/tests/run-scenarios.mjs +68 -0
- package/template/.trae/skills/yss-tactical-design/SKILL.md +33 -0
- package/template/.trae/skills/yss-tactical-design/agents/openai.yaml +4 -0
- package/template/.trae/skills/yss-tactical-design/references/output-template.md +28 -0
- package/template/.trae/skills/yss-tactical-design/references/tactical-design-contract.md +41 -0
- package/template/.trae/skills/yss-tactical-design/references/tactical-design.schema.json +31 -0
- package/template/.trae/skills/yss-tactical-design/references/validation-rules.md +13 -0
- package/template/.trae/skills/yss-tactical-design/scripts/validate-tactical-design.mjs +213 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-api-leaks-aggregate.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-missing-consistency-policy.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-missing-invariant.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-stale-upstream-ref.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/invalid-strategic-as-tactical.yaml +21 -0
- package/template/.trae/skills/yss-tactical-design/tests/fixtures/valid-tactical-design.yaml +80 -0
- package/template/.trae/skills/yss-tactical-design/tests/run-scenarios.mjs +47 -0
- package/template/AGENTS.md +2 -1
- package/template/docs/agents/digital-human-roles.md +2 -2
- package/template/docs/agents/digital-human-roles.yaml +7 -1
- package/template/docs/agents/yss-skill-registry.yaml +12 -0
- package/template/docs/architecture/templates/system-overview-design-template.md +1 -0
- package/template/docs/process/lifecycle-artifact-map.md +8 -0
- package/template/docs/process/lifecycle-registry-baseline.json +11 -1
- package/template/docs/process/lifecycle-registry.yaml +46 -2
- package/template/docs/process/schemas/digital-human-task-package.schema.json +107 -0
- package/template/docs/process/schemas/subagent-task-package.schema.json +6 -0
- package/template/docs/process/subagent-collaboration.md +2 -2
- package/template/docs/templates/subagent-task-package-template.md +39 -1
- package/template/scripts/lib/lifecycle-transition.mjs +259 -0
- package/template/scripts/lib/scenario-checks.mjs +96 -2
- package/template/scripts/lib/task-package.mjs +214 -0
- package/template/scripts/verify-digital-human-task-package +14 -0
- package/template/scripts/verify-digital-human-task-package-scenarios +8 -0
- package/template/scripts/verify-lifecycle-transition-scenarios +105 -0
- package/template/scripts/verify-subagent-task-package +8 -0
- package/template/scripts/verify-subagent-task-package-scenarios +196 -0
- package/template/scripts/verify-template +13 -2
- package/template/skills-lock.json +35 -3
- package/template.snapshot.json +4 -4
|
@@ -3,12 +3,16 @@ import path from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { parseDocument } from "../vendor/yaml.mjs";
|
|
6
|
+
import { lifecycleTransitionContract, validateImplementationEntry, validateNextRoute } from "./lifecycle-transition.mjs";
|
|
6
7
|
|
|
7
8
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
8
9
|
const read = (relative) => readFileSync(path.join(root, relative), "utf8");
|
|
9
10
|
function ensure(condition, message) { if (!condition) throw new TypeError(message); }
|
|
10
11
|
function exists(relative) { return existsSync(path.join(root, relative)); }
|
|
11
12
|
function includesAll(actual, expected) { return Array.isArray(actual) && expected.every((item) => actual.includes(item)); }
|
|
13
|
+
function hasText(value) { return typeof value === "string" && value.trim().length > 0; }
|
|
14
|
+
const virtualTicketDecompositionRef = "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml";
|
|
15
|
+
const virtualTicketDecomposition = "result_schema: workflow-execution-result-v1\nwork_unit: work-unit.ticket-decomposition\nresult: completed\nevidence_refs:\n - docs/.scratch/demo/evidence/ticket-decomposition-result.yaml\n";
|
|
12
16
|
|
|
13
17
|
function validateMattContract(data) {
|
|
14
18
|
const direct = data.entry_routing?.direct_matt_entry;
|
|
@@ -84,13 +88,52 @@ function validateWorkflowExecutionResult(payload, contract, workUnitRoutes) {
|
|
|
84
88
|
for (const field of contract.workflow_reference.required) ensure(typeof payload.workflow_reference?.[field] === "string" && payload.workflow_reference[field].trim(), `workflow_reference.${field} 无效`);
|
|
85
89
|
const workUnit = workUnitRoutes?.[payload.work_unit];
|
|
86
90
|
ensure(workUnit, `未知 Workflow Execution Result work_unit: ${payload.work_unit}`);
|
|
91
|
+
if (payload.result === "completed") {
|
|
92
|
+
const routeResult = validateNextRoute(payload.work_unit, payload.next_route);
|
|
93
|
+
ensure(routeResult.result === "allowed", `Workflow Execution Result next_route 非法: ${routeResult.blocking_signals.join(", ")}`);
|
|
94
|
+
}
|
|
87
95
|
const accepted = [workUnit.native, workUnit.compatibility].filter(Boolean);
|
|
88
96
|
ensure(contract.workflow_reference.allowed_sources.includes(payload.workflow_reference.source) && accepted.some((route) => route.source === payload.workflow_reference.source && route.skill === payload.workflow_reference.skill && route.invocation_mode === payload.workflow_reference.invocation_mode), "Workflow Execution Result workflow_reference 与 work_unit route 不匹配");
|
|
97
|
+
const workUnitRequirements = contract.work_unit_requirements?.[payload.work_unit];
|
|
98
|
+
if (workUnitRequirements && payload.result === "completed") {
|
|
99
|
+
for (const field of [...(workUnitRequirements.required ?? []), ...(workUnitRequirements.completed_requires ?? [])]) {
|
|
100
|
+
ensure(hasText(payload[field]), `Workflow Execution Result ${payload.work_unit} 缺少 ${field}`);
|
|
101
|
+
}
|
|
102
|
+
for (const field of workUnitRequirements.boolean_required ?? []) {
|
|
103
|
+
ensure(typeof payload[field] === "boolean", `Workflow Execution Result ${payload.work_unit} 缺少布尔字段 ${field}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (payload.work_unit === "work-unit.slice-implementation" && payload.result === "completed") {
|
|
107
|
+
const implementationState = {
|
|
108
|
+
tracker_kind: payload.tracker_kind,
|
|
109
|
+
predecessor_work_unit: payload.predecessor_work_unit,
|
|
110
|
+
ready_for_agent: payload.ready_for_agent,
|
|
111
|
+
ticket_decomposition_result_ref: payload.ticket_decomposition_result_ref,
|
|
112
|
+
vertical_slice_ticket_ref: payload.vertical_slice_ticket_ref,
|
|
113
|
+
vertical_slice_ticket_role: payload.vertical_slice_ticket_role,
|
|
114
|
+
vertical_slice_ticket_kind: payload.vertical_slice_ticket_kind,
|
|
115
|
+
ticket_decomposition_result: { result: payload.ticket_decomposition_result_status, evidence_refs: payload.evidence_refs },
|
|
116
|
+
vertical_slice_ticket: { ref: payload.vertical_slice_ticket_ref, role: payload.vertical_slice_ticket_role, kind: payload.vertical_slice_ticket_kind },
|
|
117
|
+
slice_contract: {
|
|
118
|
+
ticket_ref: payload.slice_contract_ticket_ref,
|
|
119
|
+
status: payload.slice_contract_status,
|
|
120
|
+
persisted: payload.slice_contract_persisted,
|
|
121
|
+
current_version: payload.slice_contract_current_version,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
const semantic = validateImplementationEntry(implementationState, {
|
|
125
|
+
// The scenario uses one explicit virtual fixture; arbitrary local refs
|
|
126
|
+
// must still pass the real readability check and cannot use a fallback.
|
|
127
|
+
exists: (ref) => exists(ref) || ref === "docs/.scratch/demo/issues/01-valid-slice.md" || ref === virtualTicketDecompositionRef,
|
|
128
|
+
read: (ref) => ref === virtualTicketDecompositionRef ? virtualTicketDecomposition : readFileSync(path.join(root, ref), "utf8"),
|
|
129
|
+
});
|
|
130
|
+
ensure(semantic.result === "allowed", `Workflow Execution Result implementation Ticket 语义非法: ${semantic.blocking_signals.join(", ")}`);
|
|
131
|
+
}
|
|
89
132
|
if (payload.result !== "completed") return;
|
|
90
133
|
for (const field of contract.completed_requires_empty) ensure(Array.isArray(payload[field]) && payload[field].length === 0, `completed 的 ${field} 必须为空`);
|
|
91
134
|
for (const field of contract.completed_requires_non_empty) ensure(Array.isArray(payload[field]) && payload[field].length > 0, `completed 的 ${field} 不能为空`);
|
|
92
135
|
if (contract.completed_requires_readable_evidence_refs) {
|
|
93
|
-
for (const reference of payload.evidence_refs) ensure(typeof reference === "string" && reference.trim() && exists(reference), `completed 证据不可读取: ${reference}`);
|
|
136
|
+
for (const reference of payload.evidence_refs) ensure(typeof reference === "string" && reference.trim() && (exists(reference) || reference === virtualTicketDecompositionRef), `completed 证据不可读取: ${reference}`);
|
|
94
137
|
}
|
|
95
138
|
if (contract.completed_requires_no_blocking_signals) ensure(Array.isArray(payload.blocking_signals) && payload.blocking_signals.length === 0, "completed 不得携带 blocking_signals");
|
|
96
139
|
}
|
|
@@ -177,6 +220,16 @@ export function runScenario(name) {
|
|
|
177
220
|
const releaseGate = registry.gates.find((gate) => gate.id === "gate.release-ready");
|
|
178
221
|
ensure(releaseGate?.requires_gates?.includes("gate.frontend-implementation-verified"), "发布就绪未依赖前端实现还原门禁");
|
|
179
222
|
const contract = parseDocument(read(".agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml"), { uniqueKeys: true }).toJS({ maxAliasCount: 0 });
|
|
223
|
+
ensure(contract.ready_for_agent?.requires_vertical_slice_ticket === true && contract.ready_for_agent?.parent_ticket_as_implementation_ref === "forbidden", "ready_for_agent 未强制垂直切片且禁止父 Ticket 实现引用");
|
|
224
|
+
ensure(includesAll(contract.ready_for_agent?.required, ["ticket_decomposition_result_ref", "vertical_slice_ticket_ref", "vertical_slice_ticket_role", "vertical_slice_ticket_kind"]), "ready_for_agent 缺少 Ticket 正式化必填字段");
|
|
225
|
+
ensure(contract.ticket_formalization?.implementation_predecessor === "work-unit.ticket-decomposition" && contract.ticket_formalization?.vertical_slice_ticket?.parent_ticket_ref_forbidden === true, "Ticket 正式化实现前置或父 Ticket 禁止规则缺失");
|
|
226
|
+
ensure(contract.transition_graph?.implementation_requires_predecessor === "work-unit.ticket-decomposition", "生命周期转换图未声明实现前置工作单元");
|
|
227
|
+
ensure(JSON.stringify(contract.transition_graph?.forbidden_shortcuts) === JSON.stringify([
|
|
228
|
+
{ from: "work-unit.spec-synthesis", to: "work-unit.slice-implementation" },
|
|
229
|
+
{ from: "work-unit.prototype-design", to: "work-unit.slice-implementation" },
|
|
230
|
+
{ from: "work-unit.technical-analysis", to: "work-unit.slice-implementation" },
|
|
231
|
+
]), "生命周期转换图缺少 Spec/原型/技术分析到实现的越级阻断");
|
|
232
|
+
ensure(lifecycleTransitionContract.next_routes["work-unit.ticket-decomposition"]?.includes("work-unit.slice-implementation"), "转换校验器未允许 Ticket 正式化后进入实现");
|
|
180
233
|
ensure(contract.release_readiness?.conditional?.ui_impact?.includes("gate.frontend-implementation-verified") && contract.frontend_implementation_plan?.acceptance?.includes("no_template_placeholders"), "发布公式或前端计划实质校验不完整");
|
|
181
234
|
const templateRejected = spawnSync("scripts/verify-frontend-implementation-evidence", ["docs/process/templates/frontend-implementation-plan-template.yaml"], { cwd: root, encoding: "utf8" });
|
|
182
235
|
ensure(templateRejected.status !== 0 && templateRejected.stderr.includes("template: false"), "前端实现计划占位模板可冒充正式批准证据");
|
|
@@ -200,6 +253,46 @@ export function runScenario(name) {
|
|
|
200
253
|
blocking_signals: []
|
|
201
254
|
};
|
|
202
255
|
validateWorkflowExecutionResult(validResult, data.workflow_execution_result, data.work_unit_routes);
|
|
256
|
+
const validTicketResult = {
|
|
257
|
+
...validResult,
|
|
258
|
+
work_unit: "work-unit.ticket-decomposition",
|
|
259
|
+
next_route: "work-unit.slice-implementation",
|
|
260
|
+
ticket_decomposition_result_ref: "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml",
|
|
261
|
+
vertical_slice_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
|
|
262
|
+
};
|
|
263
|
+
validateWorkflowExecutionResult(validTicketResult, data.workflow_execution_result, data.work_unit_routes);
|
|
264
|
+
const validImplementationResult = {
|
|
265
|
+
...validResult,
|
|
266
|
+
evidence_refs: [virtualTicketDecompositionRef, "docs/process/lifecycle-registry.yaml"],
|
|
267
|
+
work_unit: "work-unit.slice-implementation",
|
|
268
|
+
next_route: "work-unit.code-review",
|
|
269
|
+
predecessor_work_unit: "work-unit.ticket-decomposition",
|
|
270
|
+
ready_for_agent: true,
|
|
271
|
+
ticket_decomposition_result_ref: "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml",
|
|
272
|
+
ticket_decomposition_result_status: "completed",
|
|
273
|
+
vertical_slice_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
|
|
274
|
+
vertical_slice_ticket_role: "ready-for-agent",
|
|
275
|
+
vertical_slice_ticket_kind: "vertical-slice-ticket",
|
|
276
|
+
slice_contract_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
|
|
277
|
+
slice_contract_status: "approved",
|
|
278
|
+
slice_contract_persisted: true,
|
|
279
|
+
slice_contract_current_version: true,
|
|
280
|
+
};
|
|
281
|
+
validateWorkflowExecutionResult(validImplementationResult, data.workflow_execution_result, data.work_unit_routes);
|
|
282
|
+
for (const mutate of [
|
|
283
|
+
(item) => { item.vertical_slice_ticket_role = "ready-for-human"; },
|
|
284
|
+
(item) => { item.vertical_slice_ticket_kind = "parent-ticket"; },
|
|
285
|
+
(item) => { item.vertical_slice_ticket_ref = "docs/.scratch/demo/parent-ticket.md"; },
|
|
286
|
+
(item) => { item.slice_contract_ticket_ref = "docs/.scratch/demo/issues/02-other.md"; },
|
|
287
|
+
(item) => { item.predecessor_work_unit = "work-unit.spec-synthesis"; },
|
|
288
|
+
(item) => { item.ticket_decomposition_result_status = "blocked"; },
|
|
289
|
+
(item) => { item.ticket_decomposition_result_status = "needs-human"; },
|
|
290
|
+
]) {
|
|
291
|
+
const invalid = structuredClone(validImplementationResult); mutate(invalid);
|
|
292
|
+
let rejected = false;
|
|
293
|
+
try { validateWorkflowExecutionResult(invalid, data.workflow_execution_result, data.work_unit_routes); } catch { rejected = true; }
|
|
294
|
+
ensure(rejected, "实现 Workflow Execution Result 的 Ticket 语义变异未被拒绝");
|
|
295
|
+
}
|
|
203
296
|
const unavailableResult = structuredClone(validResult);
|
|
204
297
|
unavailableResult.result = "blocked";
|
|
205
298
|
unavailableResult.unavailable_skill = { skill: "yss-ui", provider: "codex", fallback: "manual-review", resolution: "needs-human" };
|
|
@@ -221,7 +314,8 @@ export function runScenario(name) {
|
|
|
221
314
|
(item) => { item.new_impacts = ["new-api"]; },
|
|
222
315
|
(item) => { item.workflow_reference.source = "untrusted/source"; },
|
|
223
316
|
(item) => { item.workflow_reference.skill = "implement"; },
|
|
224
|
-
(item) => { item.workflow_reference.invocation_mode = "reference"; }
|
|
317
|
+
(item) => { item.workflow_reference.invocation_mode = "reference"; },
|
|
318
|
+
(item) => { item.next_route = "work-unit.slice-implementation"; }
|
|
225
319
|
]) {
|
|
226
320
|
const invalid = structuredClone(validResult); mutate(invalid);
|
|
227
321
|
let rejected = false;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { parseDocument } from "../vendor/yaml.mjs";
|
|
5
|
+
import { loadDigitalHumanRoles, taskPackageDefaults } from "./digital-human-roles.mjs";
|
|
6
|
+
import { loadRegistry, ROOT } from "./lifecycle-registry.mjs";
|
|
7
|
+
import { loadMaintenanceCheckpoint, validateMaintenanceCheckpoint } from "./maintenance-intensity.mjs";
|
|
8
|
+
import { validateNextRoute } from "./lifecycle-transition.mjs";
|
|
9
|
+
|
|
10
|
+
export const TASK_PACKAGE_SCHEMA = path.join(ROOT, "docs/process/schemas/digital-human-task-package.schema.json");
|
|
11
|
+
export const LEGACY_TASK_PACKAGE_SCHEMA = path.join(ROOT, "docs/process/schemas/subagent-task-package.schema.json");
|
|
12
|
+
export const TASK_PACKAGE_REGISTRY_REF = "docs/agents/digital-human-roles.yaml";
|
|
13
|
+
export const CONTRACT_KINDS = new Set(["lifecycle-work-unit", "slice-implementation", "template-maintenance"]);
|
|
14
|
+
export const EXECUTION_STATES = new Set(["Explorer", "Drafter", "Worker", "Reviewer", "Verifier"]);
|
|
15
|
+
export const WORKFLOW_STATUSES = new Set(["not-started", "active", "paused", "resolved", "failed"]);
|
|
16
|
+
|
|
17
|
+
function fail(message) { throw new TypeError(message); }
|
|
18
|
+
function requireString(value, field) { if (typeof value !== "string" || !value.trim()) fail(`${field} 不能为空`); }
|
|
19
|
+
function equalArrays(actual, expected) { return Array.isArray(actual) && actual.length === expected.length && actual.every((value, index) => value === expected[index]); }
|
|
20
|
+
|
|
21
|
+
function parseYaml(source, label) {
|
|
22
|
+
const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
|
|
23
|
+
if (document.errors.length > 0) fail(`无法解析${label}: ${document.errors[0].message}`);
|
|
24
|
+
return document.toJS({ maxAliasCount: 0 });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function assertSafeRelativePath(value, field) {
|
|
28
|
+
requireString(value, field);
|
|
29
|
+
if (path.isAbsolute(value)) fail(`${field} 必须是仓库相对路径`);
|
|
30
|
+
const resolved = path.resolve(ROOT, value);
|
|
31
|
+
const relative = path.relative(ROOT, resolved);
|
|
32
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`)) fail(`${field} 不得越出仓库根目录`);
|
|
33
|
+
return resolved;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function assertReadableEvidenceRef(value, field) {
|
|
37
|
+
requireString(value, field);
|
|
38
|
+
if (/^https?:\/\//.test(value)) return;
|
|
39
|
+
const evidencePath = assertSafeRelativePath(value, field);
|
|
40
|
+
if (!existsSync(evidencePath)) fail(`${field} 不可读: ${value}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function loadTaskPackage(filePath) {
|
|
44
|
+
const sourcePath = path.resolve(filePath);
|
|
45
|
+
if (!existsSync(sourcePath)) fail(`任务包不存在: ${filePath}`);
|
|
46
|
+
const value = parseYaml(readFileSync(sourcePath, "utf8"), "任务包");
|
|
47
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) fail("任务包必须是对象");
|
|
48
|
+
return { value, sourcePath };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function validateTaskPackageSchema(value, schemaPath = TASK_PACKAGE_SCHEMA) {
|
|
52
|
+
const effectiveSchema = path.resolve(schemaPath) === path.resolve(LEGACY_TASK_PACKAGE_SCHEMA) ? TASK_PACKAGE_SCHEMA : schemaPath;
|
|
53
|
+
if (!existsSync(effectiveSchema)) fail(`缺少任务包 schema: ${effectiveSchema}`);
|
|
54
|
+
const validator = String.raw`
|
|
55
|
+
import json
|
|
56
|
+
import sys
|
|
57
|
+
from jsonschema import Draft202012Validator
|
|
58
|
+
schema = json.load(open(sys.argv[1], encoding="utf-8"))
|
|
59
|
+
value = json.load(sys.stdin)
|
|
60
|
+
errors = sorted(Draft202012Validator(schema).iter_errors(value), key=lambda error: list(error.absolute_path))
|
|
61
|
+
for error in errors:
|
|
62
|
+
location = ".".join(str(part) for part in error.absolute_path) or "<root>"
|
|
63
|
+
print(f"{location}: {error.message}", file=sys.stderr)
|
|
64
|
+
sys.exit(1 if errors else 0)
|
|
65
|
+
`;
|
|
66
|
+
const result = spawnSync("python3", ["-c", validator, effectiveSchema], { cwd: ROOT, encoding: "utf8", input: JSON.stringify(value) });
|
|
67
|
+
if (result.status !== 0) fail(`${result.stdout}${result.stderr}`.trim());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateSkillSource(value, registry) {
|
|
71
|
+
const defaults = taskPackageDefaults(value.role_id, registry);
|
|
72
|
+
if (value.skill_source.registry_ref !== TASK_PACKAGE_REGISTRY_REF) fail(`skill_source.registry_ref 必须为 ${TASK_PACKAGE_REGISTRY_REF}`);
|
|
73
|
+
if (value.skill_source.defaults_ref !== `taskPackageDefaults(${value.role_id})`) fail(`skill_source.defaults_ref 必须为 taskPackageDefaults(${value.role_id})`);
|
|
74
|
+
if (!equalArrays(value.skill_source.core_skills, defaults.core_skills)) fail("skill_source.core_skills 必须与角色注册表完全一致");
|
|
75
|
+
if (!equalArrays(value.skill_source.forbidden_skills, defaults.forbidden_skills)) fail("skill_source.forbidden_skills 必须与角色注册表完全一致");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function validateCommon(value, registry, lifecycle) {
|
|
79
|
+
const workUnit = lifecycle.work_units.find((item) => item.id === value.work_unit_id);
|
|
80
|
+
if (!workUnit && value.contract.kind !== "slice-implementation") fail(`未知 work_unit_id: ${value.work_unit_id}`);
|
|
81
|
+
const roleDefaults = taskPackageDefaults(value.role_id, registry);
|
|
82
|
+
const runtimeIds = new Set((registry.runtimes || []).map((runtime) => runtime.id));
|
|
83
|
+
if (!runtimeIds.has(value.runtime_id)) fail(`未知 runtime_id: ${value.runtime_id}`);
|
|
84
|
+
if (!EXECUTION_STATES.has(value.execution_state)) fail(`execution_state 无效: ${value.execution_state}`);
|
|
85
|
+
if (!WORKFLOW_STATUSES.has(value.workflow_status)) fail(`workflow_status 无效: ${value.workflow_status}`);
|
|
86
|
+
if (value.stage_id) {
|
|
87
|
+
if (!lifecycle.stages.some((stage) => stage.id === value.stage_id)) fail(`未知 stage_id: ${value.stage_id}`);
|
|
88
|
+
if (!roleDefaults.stages.includes(value.stage_id)) fail(`role_id 未覆盖 stage_id: ${value.role_id} -> ${value.stage_id}`);
|
|
89
|
+
}
|
|
90
|
+
validateSkillSource(value, registry);
|
|
91
|
+
for (const allowed of value.allowed_write_paths) assertSafeRelativePath(allowed, "allowed_write_paths 条目");
|
|
92
|
+
if (value.execution_state === "Reviewer" && value.review_context.implementation_actor_id === value.actor_id) fail("Reviewer 必须与实现者使用不同 actor_id");
|
|
93
|
+
const commands = new Set(value.verification_commands);
|
|
94
|
+
for (const result of value.verification_results) {
|
|
95
|
+
if (!commands.has(result.command)) fail(`verification_results 命令未声明: ${result.command}`);
|
|
96
|
+
assertReadableEvidenceRef(result.evidence_ref, "verification_results.evidence_ref");
|
|
97
|
+
}
|
|
98
|
+
const completed = value.workflow_status === "resolved" || value.result?.result === "completed";
|
|
99
|
+
if (completed) {
|
|
100
|
+
if (!value.result || value.result.result !== "completed") fail("resolved/完成任务必须返回 result=completed 的 Workflow Execution Result");
|
|
101
|
+
if (value.workflow_status !== "resolved") fail("result=completed 的任务包必须处于 resolved 状态");
|
|
102
|
+
for (const field of ["result_schema", "work_unit", "workflow_reference", "skill", "changed_files", "evidence_refs", "deferred_seams", "drift", "violation", "new_impacts", "stale_candidates", "next_route", "blocking_signals"]) {
|
|
103
|
+
if (value.result[field] === undefined) fail(`result=completed 时 ${field} 不能为空`);
|
|
104
|
+
}
|
|
105
|
+
if (value.result.result_schema !== "workflow-execution-result-v1") fail("result_schema 必须为 workflow-execution-result-v1");
|
|
106
|
+
if (value.result.work_unit !== value.work_unit_id) fail("Workflow Execution Result.work_unit 必须与任务包 work_unit_id 一致");
|
|
107
|
+
const routeResult = validateNextRoute(value.result.work_unit, value.result.next_route);
|
|
108
|
+
if (routeResult.result !== "allowed") fail(`Workflow Execution Result next_route 非法: ${routeResult.blocking_signals.join(", ")}`);
|
|
109
|
+
value.expected_evidence_files.forEach((ref) => assertReadableEvidenceRef(ref, "expected_evidence_files"));
|
|
110
|
+
if (value.verification_results.length === 0) fail("已完成任务必须包含 verification_results");
|
|
111
|
+
if (value.verification_results.some((result) => result.exit_code !== 0)) fail("已完成任务的验证命令必须全部成功");
|
|
112
|
+
const resultCommands = new Set(value.verification_results.map((result) => result.command));
|
|
113
|
+
if (value.verification_commands.some((command) => !resultCommands.has(command))) fail("已完成任务必须覆盖全部 verification_commands");
|
|
114
|
+
}
|
|
115
|
+
if (value.result) {
|
|
116
|
+
for (const field of ["new_impacts", "stale_candidates", "blocking_signals", "drift", "violation"]) {
|
|
117
|
+
if (value.result.result === "completed" && Array.isArray(value.result[field]) && value.result[field].length > 0) fail(`result=completed 时 ${field} 必须为空`);
|
|
118
|
+
}
|
|
119
|
+
if (value.result.result === "completed") {
|
|
120
|
+
if (!Array.isArray(value.result.evidence_refs) || value.result.evidence_refs.length === 0) fail("result=completed 时 evidence_refs 不能为空");
|
|
121
|
+
value.result.evidence_refs.forEach((ref) => assertReadableEvidenceRef(ref, "result.evidence_refs"));
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(value.result.changed_files)) {
|
|
124
|
+
for (const changed of value.result.changed_files) {
|
|
125
|
+
const changedPath = assertSafeRelativePath(changed, "changed_files 条目");
|
|
126
|
+
if (!value.allowed_write_paths.some((allowed) => {
|
|
127
|
+
const allowedPath = assertSafeRelativePath(allowed, "allowed_write_paths 条目");
|
|
128
|
+
const relative = path.relative(allowedPath, changedPath);
|
|
129
|
+
return relative === "" || (relative !== ".." && !relative.startsWith(`..${path.sep}`));
|
|
130
|
+
})) fail(`changed_files 超出 allowed_write_paths: ${changed}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return workUnit;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function validateContract(value, registry, lifecycle) {
|
|
138
|
+
const contract = value.contract;
|
|
139
|
+
const implementationWorkUnit = "work-unit.slice-implementation";
|
|
140
|
+
const workUnit = lifecycle.work_units.find((item) => item.id === value.work_unit_id);
|
|
141
|
+
if (!CONTRACT_KINDS.has(contract.kind)) fail(`未知 contract.kind: ${contract.kind}`);
|
|
142
|
+
if (!new Set(["issued", "stale", "blocked"]).has(contract.status)) fail(`contract.status 无效: ${contract.status}`);
|
|
143
|
+
if (contract.status === "stale" && value.workflow_status !== "paused") fail("stale 任务包必须暂停,待主控重新路由");
|
|
144
|
+
assertReadableEvidenceRef(contract.contract_ref, "contract.contract_ref");
|
|
145
|
+
if (contract.kind === "lifecycle-work-unit") {
|
|
146
|
+
if (value.work_unit_id === implementationWorkUnit || value.convergence.parent_work_unit === implementationWorkUnit) {
|
|
147
|
+
fail("lifecycle-work-unit 不得冒充垂直切片实现任务;实现任务必须使用 slice-implementation");
|
|
148
|
+
}
|
|
149
|
+
if (workUnit?.scope === "template-source") fail("template-source 工作单元必须使用 template-maintenance");
|
|
150
|
+
if (contract.slice_contract_ref || contract.maintenance_ref) fail("lifecycle-work-unit 不得携带专项合同引用");
|
|
151
|
+
assertReadableEvidenceRef(contract.lifecycle_ref, "contract.lifecycle_ref");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (contract.kind === "template-maintenance") {
|
|
155
|
+
if (contract.slice_contract_ref || contract.lifecycle_ref) fail("template-maintenance 不得携带其他合同引用");
|
|
156
|
+
const checkpointPath = assertSafeRelativePath(contract.maintenance_ref, "contract.maintenance_ref");
|
|
157
|
+
if (!existsSync(checkpointPath)) fail(`维护 checkpoint 不存在: ${contract.maintenance_ref}`);
|
|
158
|
+
validateMaintenanceCheckpoint(loadMaintenanceCheckpoint(contract.maintenance_ref));
|
|
159
|
+
if (lifecycle.work_units.find((item) => item.id === value.work_unit_id)?.scope !== "template-source") fail("template-maintenance 必须绑定 template-source work unit");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (contract.lifecycle_ref || contract.maintenance_ref) fail("slice-implementation 不得携带其他合同引用");
|
|
163
|
+
if (!/^stage\.vertical-slice-implementation$/.test(value.stage_id)) fail("slice-implementation 必须绑定垂直切片实现阶段");
|
|
164
|
+
if (value.convergence.parent_work_unit !== implementationWorkUnit) fail("slice-implementation 必须汇合到 work-unit.slice-implementation");
|
|
165
|
+
if (/^https?:\/\//.test(contract.slice_contract_ref)) fail("slice_contract_ref 必须引用本地已持久化的 Slice Implementation Contract");
|
|
166
|
+
const contractPath = assertSafeRelativePath(contract.slice_contract_ref, "contract.slice_contract_ref");
|
|
167
|
+
if (!existsSync(contractPath)) fail(`Slice Implementation Contract 不存在: ${contract.slice_contract_ref}`);
|
|
168
|
+
const document = parseYaml(readFileSync(contractPath, "utf8"), "Slice Implementation Contract");
|
|
169
|
+
const slice = document.slice_contract || document;
|
|
170
|
+
if (slice.schema_version !== 1 || typeof slice.slice_id !== "string" || !slice.lifecycle_refs || !slice.readiness || !Array.isArray(slice.work_units)) fail("Slice Implementation Contract 缺少 schema_version/slice_id/lifecycle_refs/readiness/work_units");
|
|
171
|
+
if (slice.contract_id !== contract.contract_id || slice.contract_version !== contract.contract_version || slice.status !== "approved") fail("Slice Contract 必须是当前 approved 版本");
|
|
172
|
+
const sliceUnit = slice.work_units.find((item) => item && item.id === value.work_unit_id);
|
|
173
|
+
if (!sliceUnit) fail(`Slice Implementation Contract 缺少 work_unit_id: ${value.work_unit_id}`);
|
|
174
|
+
for (const field of ["role_id", "runtime_id", "task_package_ref", "contract_id", "contract_version", "allowed_write_paths"]) if (sliceUnit[field] === undefined) fail(`Slice Contract work_unit 缺少 ${field}`);
|
|
175
|
+
if (sliceUnit.role_id !== value.role_id || sliceUnit.runtime_id !== value.runtime_id || sliceUnit.contract_id !== contract.contract_id || sliceUnit.contract_version !== contract.contract_version || sliceUnit.task_package_ref !== value.work_unit_id) fail("任务包与 Slice Contract work_unit 不一致");
|
|
176
|
+
for (const allowed of value.allowed_write_paths) if (!sliceUnit.allowed_write_paths.includes(allowed)) fail(`allowed_write_paths 未获 Slice Contract 授权: ${allowed}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function validateTaskPackage(value, { rolesDoc, lifecycleDoc } = {}) {
|
|
180
|
+
validateTaskPackageSchema(value);
|
|
181
|
+
const registry = rolesDoc || loadDigitalHumanRoles();
|
|
182
|
+
const lifecycle = lifecycleDoc || loadRegistry();
|
|
183
|
+
validateCommon(value, registry, lifecycle);
|
|
184
|
+
validateContract(value, registry, lifecycle);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function generateTaskPackageDefaults(roleId, overrides = {}, { rolesDoc } = {}) {
|
|
189
|
+
const defaults = taskPackageDefaults(roleId, rolesDoc || loadDigitalHumanRoles());
|
|
190
|
+
return {
|
|
191
|
+
schema_version: 1,
|
|
192
|
+
role_id: defaults.role_id,
|
|
193
|
+
skill_source: {
|
|
194
|
+
registry_ref: TASK_PACKAGE_REGISTRY_REF,
|
|
195
|
+
defaults_ref: `taskPackageDefaults(${roleId})`,
|
|
196
|
+
core_skills: defaults.core_skills,
|
|
197
|
+
forbidden_skills: defaults.forbidden_skills
|
|
198
|
+
},
|
|
199
|
+
...overrides
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function validateTaskPackageSet(packages, options = {}) {
|
|
204
|
+
if (!Array.isArray(packages) || packages.length === 0) fail("任务包集合不能为空");
|
|
205
|
+
packages.forEach((pkg) => validateTaskPackage(pkg, options));
|
|
206
|
+
const slicePackages = packages.filter((pkg) => pkg.contract.kind === "slice-implementation");
|
|
207
|
+
if (slicePackages.length > 0) {
|
|
208
|
+
const contracts = new Set(slicePackages.map((pkg) => `${pkg.contract.contract_id}@${pkg.contract.contract_version}`));
|
|
209
|
+
if (contracts.size !== 1) fail("同一切片的任务包必须消费同一 contract_id/contract_version");
|
|
210
|
+
const workers = new Set(slicePackages.filter((pkg) => pkg.execution_state === "Worker").map((pkg) => pkg.actor_id));
|
|
211
|
+
slicePackages.filter((pkg) => pkg.execution_state === "Reviewer").forEach((pkg) => { if (workers.has(pkg.actor_id)) fail("Reviewer 不得与实现者使用同一 actor_id"); });
|
|
212
|
+
}
|
|
213
|
+
return packages;
|
|
214
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { loadTaskPackage, validateTaskPackage } from "./lib/task-package.mjs";
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const { positionals } = parseArgs({ allowPositionals: true, strict: true });
|
|
7
|
+
if (positionals.length !== 1) throw new TypeError("用法: scripts/verify-digital-human-task-package <task-package.yaml|task-package.json>");
|
|
8
|
+
const { value } = loadTaskPackage(positionals[0]);
|
|
9
|
+
validateTaskPackage(value);
|
|
10
|
+
process.stdout.write("数字人任务包验证通过\n");
|
|
11
|
+
} catch (error) {
|
|
12
|
+
process.stderr.write(`数字人任务包验证失败: ${error.message}\n`);
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
const result = spawnSync(process.execPath, [path.join(root, "scripts/verify-subagent-task-package-scenarios"), ...process.argv.slice(2)], { stdio: "inherit" });
|
|
8
|
+
process.exitCode = result.status ?? 1;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { validateImplementationEntry, validateNextRoute, validateTicketFormalization } from "./lib/lifecycle-transition.mjs";
|
|
4
|
+
|
|
5
|
+
const exists = (ref) => [
|
|
6
|
+
"docs/process/lifecycle-registry.yaml",
|
|
7
|
+
"docs/.scratch/demo/issues/01-valid-slice.md",
|
|
8
|
+
"docs/.scratch/demo/evidence/ticket-decomposition-result.yaml",
|
|
9
|
+
].includes(ref);
|
|
10
|
+
const decompositionResult = `result_schema: workflow-execution-result-v1\nwork_unit: work-unit.ticket-decomposition\nresult: completed\nevidence_refs:\n - docs/.scratch/demo/evidence/ticket-decomposition-result.yaml\n`;
|
|
11
|
+
const read = (ref) => ref === "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml" ? decompositionResult : "";
|
|
12
|
+
const options = { exists, read };
|
|
13
|
+
|
|
14
|
+
const validTicketState = {
|
|
15
|
+
predecessor_work_unit: "work-unit.ticket-decomposition",
|
|
16
|
+
tracker_kind: "local-markdown",
|
|
17
|
+
ticket_decomposition_result_ref: "docs/.scratch/demo/evidence/ticket-decomposition-result.yaml",
|
|
18
|
+
vertical_slice_ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
|
|
19
|
+
vertical_slice_ticket_role: "ready-for-agent",
|
|
20
|
+
vertical_slice_ticket_kind: "vertical-slice-ticket",
|
|
21
|
+
ticket_decomposition_result: {
|
|
22
|
+
result: "completed",
|
|
23
|
+
evidence_refs: ["docs/.scratch/demo/evidence/ticket-decomposition-result.yaml"],
|
|
24
|
+
},
|
|
25
|
+
vertical_slice_ticket: {
|
|
26
|
+
ref: "docs/.scratch/demo/issues/01-valid-slice.md",
|
|
27
|
+
kind: "vertical-slice-ticket",
|
|
28
|
+
role: "ready-for-agent",
|
|
29
|
+
},
|
|
30
|
+
slice_contract: {
|
|
31
|
+
ticket_ref: "docs/.scratch/demo/issues/01-valid-slice.md",
|
|
32
|
+
status: "approved",
|
|
33
|
+
persisted: true,
|
|
34
|
+
current_version: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const valid = validateTicketFormalization(validTicketState, options);
|
|
39
|
+
assert.equal(valid.result, "allowed");
|
|
40
|
+
|
|
41
|
+
const implementation = validateImplementationEntry({
|
|
42
|
+
...validTicketState,
|
|
43
|
+
ready_for_agent: true,
|
|
44
|
+
}, options);
|
|
45
|
+
assert.equal(implementation.result, "allowed");
|
|
46
|
+
|
|
47
|
+
const cases = [
|
|
48
|
+
["missing decomposition result ref", { ...validTicketState, ticket_decomposition_result_ref: undefined }, "ticket-decomposition-result-ref-missing"],
|
|
49
|
+
["missing slice ref", { ...validTicketState, vertical_slice_ticket_ref: undefined }, "vertical-slice-ticket-ref-missing"],
|
|
50
|
+
["missing slice role", { ...validTicketState, vertical_slice_ticket_role: undefined }, "vertical-slice-ticket-role-missing"],
|
|
51
|
+
["missing slice kind", { ...validTicketState, vertical_slice_ticket_kind: undefined }, "vertical-slice-ticket-kind-missing"],
|
|
52
|
+
["missing vertical slice", { ...validTicketState, vertical_slice_ticket: undefined }, "vertical-slice-ticket-required"],
|
|
53
|
+
["parent ticket reference", { ...validTicketState, vertical_slice_ticket: { ref: "docs/.scratch/demo/parent-ticket.md", kind: "parent-ticket", role: "ready-for-agent" } }, "parent-ticket-forbidden"],
|
|
54
|
+
["human-ready slice", { ...validTicketState, vertical_slice_ticket: { ...validTicketState.vertical_slice_ticket, role: "ready-for-human" } }, "ticket-not-ready-for-agent"],
|
|
55
|
+
["uncompleted decomposition", { ...validTicketState, ticket_decomposition_result: { result: "blocked", evidence_refs: [] } }, "ticket-decomposition-incomplete"],
|
|
56
|
+
["needs-human decomposition", { ...validTicketState, ticket_decomposition_result: { result: "needs-human", evidence_refs: [] } }, "ticket-decomposition-incomplete"],
|
|
57
|
+
["unreadable decomposition evidence", { ...validTicketState, ticket_decomposition_result: { ...validTicketState.ticket_decomposition_result, evidence_refs: ["docs/.scratch/demo/missing-evidence.md"] } }, "ticket-formalization-evidence-missing"],
|
|
58
|
+
["wrong slice kind", { ...validTicketState, vertical_slice_ticket: { ...validTicketState.vertical_slice_ticket, kind: "parent-ticket" }, vertical_slice_ticket_kind: "parent-ticket" }, "parent-ticket-forbidden"],
|
|
59
|
+
["unapproved contract", { ...validTicketState, slice_contract: { ...validTicketState.slice_contract, status: "draft" } }, "slice-contract-not-approved"],
|
|
60
|
+
["unpersisted contract", { ...validTicketState, slice_contract: { ...validTicketState.slice_contract, persisted: false } }, "slice-contract-not-persisted"],
|
|
61
|
+
["stale formalization", { ...validTicketState, stale: true }, "ticket-formalization-stale"],
|
|
62
|
+
["wrong implementation predecessor", { ...validTicketState, predecessor_work_unit: "work-unit.spec-synthesis" }, "invalid-implementation-predecessor"],
|
|
63
|
+
["contract bound to another ticket", { ...validTicketState, slice_contract: { ...validTicketState.slice_contract, ticket_ref: "docs/.scratch/demo/issues/02-other.md" } }, "slice-contract-ticket-mismatch"],
|
|
64
|
+
["unreadable slice", { ...validTicketState, vertical_slice_ticket: { ...validTicketState.vertical_slice_ticket, ref: "docs/.scratch/demo/issues/03-missing.md" } }, "vertical-slice-ticket-unreadable"],
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
for (const [name, state, signal] of cases) {
|
|
68
|
+
const result = validateImplementationEntry(state, options);
|
|
69
|
+
assert.equal(result.result, "blocked", name);
|
|
70
|
+
assert.ok(result.blocking_signals.includes(signal), `${name}: missing ${signal}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const parentOnly = validateTicketFormalization({
|
|
74
|
+
...validTicketState,
|
|
75
|
+
vertical_slice_ticket: { ref: "docs/.scratch/demo/parent-ticket.md", kind: "parent-ticket", role: "ready-for-human" },
|
|
76
|
+
}, options);
|
|
77
|
+
assert.equal(parentOnly.result, "blocked");
|
|
78
|
+
|
|
79
|
+
assert.equal(validateNextRoute("work-unit.ticket-decomposition", "work-unit.slice-implementation").result, "allowed");
|
|
80
|
+
for (const current of ["work-unit.spec-synthesis", "work-unit.prototype-design", "work-unit.technical-analysis"]) {
|
|
81
|
+
const result = validateNextRoute(current, "work-unit.slice-implementation");
|
|
82
|
+
assert.equal(result.result, "blocked", current);
|
|
83
|
+
assert.ok(result.blocking_signals.includes("ticket-formalization-required"), current);
|
|
84
|
+
}
|
|
85
|
+
assert.equal(validateNextRoute("work-unit.spec-synthesis", "work-unit.ticket-decomposition").result, "allowed");
|
|
86
|
+
assert.equal(validateNextRoute("work-unit.intensity-aware-verification", "work-unit.intensity-aware-review").result, "allowed");
|
|
87
|
+
assert.equal(validateNextRoute("work-unit.release-and-rollback", null).result, "allowed");
|
|
88
|
+
const arbitraryReadable = validateTicketFormalization({
|
|
89
|
+
...validTicketState,
|
|
90
|
+
ticket_decomposition_result_ref: "docs/process/lifecycle-registry.yaml",
|
|
91
|
+
ticket_decomposition_result: { result: "completed", evidence_refs: ["docs/process/lifecycle-registry.yaml"] },
|
|
92
|
+
}, options);
|
|
93
|
+
assert.equal(arbitraryReadable.result, "blocked");
|
|
94
|
+
assert.ok(arbitraryReadable.blocking_signals.includes("ticket-decomposition-incomplete"));
|
|
95
|
+
assert.equal(validateTicketFormalization({
|
|
96
|
+
...validTicketState,
|
|
97
|
+
tracker_kind: "github",
|
|
98
|
+
ticket_decomposition_result_ref: "https://github.com/example/project/issues/1",
|
|
99
|
+
ticket_decomposition_result: { result: "completed", evidence_refs: ["https://github.com/example/project/issues/1"] },
|
|
100
|
+
vertical_slice_ticket_ref: "https://github.com/example/project/issues/1",
|
|
101
|
+
vertical_slice_ticket: { ref: "https://github.com/example/project/issues/1", kind: "vertical-slice-ticket", role: "ready-for-agent" },
|
|
102
|
+
slice_contract: { ...validTicketState.slice_contract, ticket_ref: "https://github.com/example/project/issues/1" },
|
|
103
|
+
}, { ...options, exists: (ref) => ref === "https://github.com/example/project/issues/1", read: (ref) => ref === "https://github.com/example/project/issues/1" ? decompositionResult.replace("docs/.scratch/demo/evidence/ticket-decomposition-result.yaml", "https://github.com/example/project/issues/1") : "" }).result, "allowed");
|
|
104
|
+
|
|
105
|
+
process.stdout.write("生命周期转换压力场景验证通过\n");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
7
|
+
const result = spawnSync(process.execPath, [path.join(root, "scripts/verify-digital-human-task-package"), ...process.argv.slice(2)], { stdio: "inherit" });
|
|
8
|
+
process.exitCode = result.status ?? 1;
|