create-yss-spec 3.3.6 → 3.3.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/README.md +12 -13
- package/package.json +1 -1
- package/src/template/instance-runtime.js +8 -2
- package/src/validation/snapshot.js +7 -0
- package/template/.agents/skills/.strategic-design-skills-manifest.json +6 -4
- package/template/.agents/skills/archify/SKILL.md +1 -1
- package/template/.agents/skills/implementation-repo-onboarding/SKILL.md +2 -2
- package/template/.agents/skills/yss-application/SKILL.md +1 -1
- package/template/.agents/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.agents/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
- package/template/.agents/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
- package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
- package/template/.agents/skills/yss-prototype-stage/SKILL.md +10 -1
- package/template/.agents/skills/yss-stage-decision/SKILL.md +13 -4
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
- package/template/.agents/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
- package/template/.agents/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
- package/template/.agents/skills/yss-stage-decision/references/validation-rules.md +2 -2
- package/template/.agents/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
- package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
- package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
- package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
- package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
- package/template/.agents/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.agents/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
- package/template/.agents/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
- package/template/.agents/skills/yss-technical-design/references/technical-design.schema.json +288 -10
- package/template/.agents/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
- package/template/.agents/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
- package/template/.agents/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
- package/template/.agents/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
- package/template/.agents/skills/yss-web-controller/SKILL.md +1 -1
- package/template/.agents/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.agents/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.codex/skills/archify/SKILL.md +1 -1
- package/template/.codex/skills/implementation-repo-onboarding/SKILL.md +2 -2
- package/template/.codex/skills/yss-application/SKILL.md +1 -1
- package/template/.codex/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.codex/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
- package/template/.codex/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
- package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
- package/template/.codex/skills/yss-prototype-stage/SKILL.md +10 -1
- package/template/.codex/skills/yss-stage-decision/SKILL.md +13 -4
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
- package/template/.codex/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
- package/template/.codex/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
- package/template/.codex/skills/yss-stage-decision/references/validation-rules.md +2 -2
- package/template/.codex/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
- package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
- package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
- package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
- package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
- package/template/.codex/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.codex/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
- package/template/.codex/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
- package/template/.codex/skills/yss-technical-design/references/technical-design.schema.json +288 -10
- package/template/.codex/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
- package/template/.codex/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
- package/template/.codex/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
- package/template/.codex/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
- package/template/.codex/skills/yss-web-controller/SKILL.md +1 -1
- package/template/.codex/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.codex/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.cursor/skills/archify/SKILL.md +1 -1
- package/template/.cursor/skills/implementation-repo-onboarding/SKILL.md +2 -2
- package/template/.cursor/skills/yss-application/SKILL.md +1 -1
- package/template/.cursor/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.cursor/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
- package/template/.cursor/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
- package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
- package/template/.cursor/skills/yss-prototype-stage/SKILL.md +10 -1
- package/template/.cursor/skills/yss-stage-decision/SKILL.md +13 -4
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
- package/template/.cursor/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
- package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
- package/template/.cursor/skills/yss-stage-decision/references/validation-rules.md +2 -2
- package/template/.cursor/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
- package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
- package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
- package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
- package/template/.cursor/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.cursor/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
- package/template/.cursor/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
- package/template/.cursor/skills/yss-technical-design/references/technical-design.schema.json +288 -10
- package/template/.cursor/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
- package/template/.cursor/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
- package/template/.cursor/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
- package/template/.cursor/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
- package/template/.cursor/skills/yss-web-controller/SKILL.md +1 -1
- package/template/.cursor/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.cursor/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.pi/skills/archify/SKILL.md +1 -1
- package/template/.pi/skills/implementation-repo-onboarding/SKILL.md +2 -2
- package/template/.pi/skills/yss-application/SKILL.md +1 -1
- package/template/.pi/skills/yss-application/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.pi/skills/yss-application/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +2 -2
- package/template/.pi/skills/yss-implementation-contract-compiler/references/compiler-contract.yaml +4 -0
- package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +6 -1
- package/template/.pi/skills/yss-prototype-stage/SKILL.md +10 -1
- package/template/.pi/skills/yss-stage-decision/SKILL.md +13 -4
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
- package/template/.pi/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
- package/template/.pi/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
- package/template/.pi/skills/yss-stage-decision/references/validation-rules.md +2 -2
- package/template/.pi/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
- package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
- package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
- package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
- package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
- package/template/.pi/skills/yss-technical-design/SKILL.md +2 -0
- package/template/.pi/skills/yss-technical-design/references/engineering-design.schema.json +224 -0
- package/template/.pi/skills/yss-technical-design/references/technical-design-common.schema.json +6 -0
- package/template/.pi/skills/yss-technical-design/references/technical-design.schema.json +288 -10
- package/template/.pi/skills/yss-technical-design/scripts/engineering-design.mjs +30 -0
- package/template/.pi/skills/yss-technical-design/scripts/generate-schema.mjs +2 -1
- package/template/.pi/skills/yss-technical-design/scripts/validate-technical-design.mjs +20 -8
- package/template/.pi/skills/yss-technical-design/tests/engineering-scenarios.mjs +12 -0
- package/template/.pi/skills/yss-web-controller/SKILL.md +1 -1
- package/template/.pi/skills/yss-web-controller/references/profiles/existing-domain-driven-maven.md +5 -0
- package/template/.pi/skills/yss-web-controller/references/profiles/existing-layered-mvc-maven.md +5 -0
- package/template/README.md +4 -6
- package/template/docs/agents/backend-architecture-profiles.md +4 -0
- package/template/docs/agents/yss-skill-registry.yaml +13 -0
- package/template/docs/design/templates/prototype-confirmation-template.md +2 -2
- package/template/docs/design/templates/prototype-review-checklist.md +2 -2
- package/template/docs/plan/templates/market-analysis-template.md +3 -3
- package/template/docs/plan/templates/user-pain-points-template.md +3 -3
- package/template/docs/process/delivery-preflight.md +72 -0
- package/template/docs/process/document-writing.md +5 -1
- package/template/docs/process/existing-backend-architecture.md +31 -0
- package/template/docs/process/existing-ui-baseline.md +40 -0
- package/template/docs/process/frontend-backend-delivery.md +21 -5
- package/template/docs/process/implementation-repo-integration.md +6 -0
- package/template/docs/process/schemas/backend-architecture-identity.schema.json +111 -14
- package/template/docs/process/schemas/backend-delivery.schema.json +4 -0
- package/template/docs/process/schemas/delivery-preflight-input.schema.json +158 -0
- package/template/docs/process/schemas/delivery-preflight-result.schema.json +159 -0
- package/template/docs/process/schemas/digital-human-task-package.schema.json +1 -1
- package/template/docs/process/schemas/existing-ui-baseline.schema.json +365 -0
- package/template/docs/process/schemas/frontend-delivery-acceptance-v1.schema.json +200 -0
- package/template/docs/process/schemas/frontend-delivery-acceptance-v3.schema.json +308 -0
- package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +37 -3
- package/template/docs/process/schemas/frontend-strategic-preflight-v2.schema.json +114 -0
- package/template/docs/process/schemas/frontend-strategic-preflight.schema.json +32 -0
- package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +2 -1
- package/template/docs/process/schemas/strategic-consumer-feedback.schema.json +40 -0
- package/template/docs/process/schemas/strategic-design-handoff-v3.schema.json +743 -0
- package/template/docs/process/schemas/strategic-design-handoff-v4.schema.json +712 -0
- package/template/docs/process/schemas/strategic-design-handoff-v5.schema.json +932 -0
- package/template/docs/process/schemas/strategic-feedback-adjudication.schema.json +42 -0
- package/template/docs/process/schemas/strategic-handoff-domain-strategy-v3.schema.json +216 -0
- package/template/docs/process/schemas/strategic-handoff-export-v2.schema.json +378 -0
- package/template/docs/process/schemas/strategic-handoff-import-receipt.schema.json +39 -0
- package/template/docs/process/schemas/strategic-handoff-stage-decision-package-v3.schema.json +148 -0
- package/template/docs/process/strategic-handoff-package.md +51 -10
- package/template/docs/process/templates/lifecycle-checkpoint-template.yaml +1 -0
- package/template/docs/process/templates/stage-checkpoint-template.md +1 -0
- package/template/docs/user-guide//346/212/200/346/234/257/350/256/276/350/256/241/347/224/250/346/210/267/346/214/207/345/215/227.md +43 -0
- package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214.md +83 -67
- package/template/docs/user-guide//347/224/250/346/210/267/346/211/213/345/206/214/347/264/242/345/274/225.md +9 -10
- package/template/docs/user-guide//350/256/276/345/244/207/345/200/237/347/224/250/350/264/257/347/251/277/346/241/210/344/276/213.md +12 -9
- package/template/scripts/backend-delivery +20 -4
- package/template/scripts/lib/approval-record.mjs +2 -1
- package/template/scripts/lib/approved-execution-context.mjs +106 -0
- package/template/scripts/lib/backend-architecture.mjs +12 -0
- package/template/scripts/lib/backend-delivery.mjs +21 -5
- package/template/scripts/lib/delivery-preflight.mjs +238 -0
- package/template/scripts/lib/existing-backend-architecture.mjs +139 -0
- package/template/scripts/lib/existing-ui-baseline.mjs +84 -0
- package/template/scripts/lib/frontend-delivery-boundary.mjs +20 -4
- package/template/scripts/lib/frontend-delivery.mjs +76 -4
- package/template/scripts/lib/implementation-contract-compiler.mjs +19 -5
- package/template/scripts/lib/json-schema.mjs +19 -4
- package/template/scripts/lib/profile-skill-sync.mjs +26 -0
- package/template/scripts/lib/strategic-feedback.mjs +33 -0
- package/template/scripts/lib/strategic-handoff-consumption.mjs +11 -5
- package/template/scripts/lib/strategic-handoff-design-targets.mjs +2 -3
- package/template/scripts/lib/strategic-handoff-io.mjs +11 -0
- package/template/scripts/lib/strategic-handoff.mjs +143 -34
- package/template/scripts/lib/task-package.mjs +11 -3
- package/template/scripts/lib/technical-design-boundary.mjs +9 -1
- package/template/scripts/lib/ui-baseline.mjs +26 -0
- package/template/scripts/lib/user-decision-reuse.mjs +38 -0
- package/template/scripts/lib/user-decision.mjs +4 -1
- package/template/scripts/preflight-delivery +14 -0
- package/template/scripts/strategic-feedback +10 -0
- package/template/scripts/strategic-handoff +19 -4
- package/template/scripts/sync-profile-skills +4 -5
- package/template/scripts/verify-frontend-strategic-preflight +12 -0
- package/template/skills-lock.json +18 -18
- package/template.manifest.json +1 -0
- package/template.snapshot.json +6 -5
- package/template/docs/user-guide//346/210/230/346/234/257/350/256/276/350/256/241/345/255/220/351/241/271/347/233/256/347/224/250/346/210/267/346/211/213/345/206/214.md +0 -35
package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { verifyContextSnapshot } from "../../../../scripts/lib/context-contract.
|
|
|
10
10
|
|
|
11
11
|
const required = ["schema_version", "stage_decision_id", "package_version", "status", "problem_statement", "target_users", "mvp", "non_goals", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "unresolved_items", "context_snapshot", "domain_strategy_ref", "impact_assessment", "downstream_mapping", "evidence_refs", "approval"];
|
|
12
12
|
const idPattern = /^stage-decision\.[a-z0-9][a-z0-9-]*$/;
|
|
13
|
+
const consumerCapabilities = new Set(["backend-technical-design", "frontend-engineering-design", "delivery-coordination"]);
|
|
13
14
|
|
|
14
15
|
function fail(errors) {
|
|
15
16
|
process.stderr.write(`${JSON.stringify({ result: "blocked", errors }, null, 2)}\n`);
|
|
@@ -18,7 +19,8 @@ function fail(errors) {
|
|
|
18
19
|
function nonEmpty(value) { return typeof value === "string" && value.trim().length > 0; }
|
|
19
20
|
function requireField(object, field, path, errors) { if (!(field in object) || object[field] === null || object[field] === undefined) errors.push(`${path}.${field} 缺失`); }
|
|
20
21
|
function requireString(object, field, path, errors) { requireField(object, field, path, errors); if (field in object && !nonEmpty(object[field])) errors.push(`${path}.${field} 必须是非空字符串`); }
|
|
21
|
-
function requireArray(object, field, path, errors, min = 0) { requireField(object, field, path, errors); if (field in object && (!Array.isArray(object[field]) || object[field].length < min)) errors.push(`${path}.${field} 必须是至少 ${min} 项的数组`); if (field in object && Array.isArray(object[field])) object[field].forEach((item, index) => { if (!nonEmpty(item)) errors.push(`${path}.${field}[${index}] 必须是非空字符串`); }); }
|
|
22
|
+
function requireArray(object, field, path, errors, min = 0, itemKind = "string") { requireField(object, field, path, errors); if (field in object && (!Array.isArray(object[field]) || object[field].length < min)) errors.push(`${path}.${field} 必须是至少 ${min} 项的数组`); if (field in object && Array.isArray(object[field]) && itemKind === "string") object[field].forEach((item, index) => { if (!nonEmpty(item)) errors.push(`${path}.${field}[${index}] 必须是非空字符串`); }); }
|
|
23
|
+
function unique(items, label, errors) { const seen = new Set(); for (const item of items) { if (seen.has(item)) errors.push(`${label} 重复: ${item}`); seen.add(item); } }
|
|
22
24
|
function canonical(value) { if (Array.isArray(value)) return value.map(canonical); if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonical(value[key])])); return value; }
|
|
23
25
|
function digest(value) { return `sha256:${createHash("sha256").update(JSON.stringify(canonical(value))).digest("hex")}`; }
|
|
24
26
|
|
|
@@ -27,12 +29,13 @@ async function validate(data, contextRoot) {
|
|
|
27
29
|
if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
|
|
28
30
|
if (data.schema_version === 1) return ["migration-required: stage decision package v1 必须迁移到 v2 context_snapshot"];
|
|
29
31
|
for (const field of required) requireField(data, field, "root", errors);
|
|
30
|
-
if (data.schema_version
|
|
32
|
+
if (![2, 3].includes(data.schema_version)) errors.push("schema_version 不受支持;支持版本: 2, 3;新交付请迁移到 3");
|
|
31
33
|
if (!idPattern.test(String(data.stage_decision_id ?? ""))) errors.push("stage_decision_id 格式非法");
|
|
32
34
|
if (!/^v[0-9]+$/.test(String(data.package_version ?? ""))) errors.push("package_version 必须形如 v1");
|
|
33
35
|
if (!["draft", "ready-for-human", "approved", "stale", "blocked"].includes(data.status)) errors.push("status 非法");
|
|
34
36
|
for (const field of ["problem_statement"]) requireString(data, field, "root", errors);
|
|
35
|
-
for (const field of ["target_users", "mvp", "non_goals", "
|
|
37
|
+
for (const field of ["target_users", "mvp", "non_goals", "evidence_refs"]) requireArray(data, field, "root", errors, ["target_users", "mvp", "evidence_refs"].includes(field) ? 1 : 0);
|
|
38
|
+
for (const field of ["success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints"]) requireArray(data, field, "root", errors, ["success_criteria", "test_seams", "confirmed_decisions"].includes(field) ? 1 : 0, data.schema_version === 3 ? "object" : "string");
|
|
36
39
|
|
|
37
40
|
const unresolved = Array.isArray(data.unresolved_items) ? data.unresolved_items : [];
|
|
38
41
|
for (const [index, item] of unresolved.entries()) {
|
|
@@ -56,9 +59,38 @@ async function validate(data, contextRoot) {
|
|
|
56
59
|
if (strategy.domain_version !== domainRef.domain_version) errors.push("domain_strategy_ref.domain_version 与实际文件不一致");
|
|
57
60
|
if (strategy.status !== domainRef.status) errors.push("domain_strategy_ref.status 与实际文件不一致");
|
|
58
61
|
if (domainRef.digest !== digest(strategy)) errors.push("domain_strategy_ref.digest 与实际领域战略内容不一致");
|
|
62
|
+
if (data.schema_version === 3 && strategy.schema_version !== 3) errors.push("Stage Decision v3 必须引用 Domain Strategy v3");
|
|
59
63
|
}
|
|
60
64
|
} catch (error) { errors.push(`domain_strategy_ref.persisted_ref 无法读取: ${error.message}`); }
|
|
61
65
|
}
|
|
66
|
+
|
|
67
|
+
if (data.schema_version === 3) {
|
|
68
|
+
const sourceIds = new Set([
|
|
69
|
+
...(strategy?.rule_catalog ?? []).map((item) => item?.rule_id),
|
|
70
|
+
...(strategy?.scenarios ?? []).map((item) => item?.scenario_id),
|
|
71
|
+
...(strategy?.invariants ?? []).map((item) => item?.invariant_id)
|
|
72
|
+
].filter(Boolean));
|
|
73
|
+
const recordFields = {
|
|
74
|
+
success_criteria: "success-criterion",
|
|
75
|
+
test_seams: "test-seam",
|
|
76
|
+
confirmed_decisions: "decision",
|
|
77
|
+
assumptions: "assumption",
|
|
78
|
+
constraints: "constraint"
|
|
79
|
+
};
|
|
80
|
+
const recordIds = [];
|
|
81
|
+
for (const [field, prefix] of Object.entries(recordFields)) {
|
|
82
|
+
for (const [index, record] of (Array.isArray(data[field]) ? data[field] : []).entries()) {
|
|
83
|
+
const path = `${field}[${index}]`;
|
|
84
|
+
for (const name of ["id", "statement"]) requireString(record ?? {}, name, path, errors);
|
|
85
|
+
requireArray(record ?? {}, "evidence_refs", path, errors, 1);
|
|
86
|
+
if (!new RegExp(`^${prefix}\\.[a-z0-9][a-z0-9-]*$`).test(String(record?.id ?? ""))) errors.push(`${path}.id 格式非法`);
|
|
87
|
+
for (const reference of record?.source_refs ?? []) if (!sourceIds.has(reference)) errors.push(`${path}.source_refs 未在领域战略中声明: ${reference}`);
|
|
88
|
+
if (record?.status && !["confirmed", "candidate", "stale"].includes(record.status)) errors.push(`${path}.status 非法`);
|
|
89
|
+
if (record?.id) recordIds.push(record.id);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
unique(recordIds, "stage record id", errors);
|
|
93
|
+
}
|
|
62
94
|
try {
|
|
63
95
|
const contextIds = Array.isArray(strategy?.contexts) ? strategy.contexts.map((item) => item?.context_id).filter(Boolean) : [];
|
|
64
96
|
verifyContextSnapshot(data.context_snapshot, { root: contextRoot, allowedContextIds: contextIds });
|
|
@@ -73,9 +105,21 @@ async function validate(data, contextRoot) {
|
|
|
73
105
|
}
|
|
74
106
|
const mappings = Array.isArray(data.downstream_mapping) ? data.downstream_mapping : [];
|
|
75
107
|
if (mappings.length < 1) errors.push("downstream_mapping 至少需要一项");
|
|
108
|
+
if (data.schema_version === 3) unique(mappings.map((item) => item?.mapping_id).filter(Boolean), "mapping_id", errors);
|
|
109
|
+
const strategySourceIds = new Set([
|
|
110
|
+
...(strategy?.rule_catalog ?? []).map((item) => item?.rule_id),
|
|
111
|
+
...(strategy?.scenarios ?? []).map((item) => item?.scenario_id),
|
|
112
|
+
...(strategy?.invariants ?? []).map((item) => item?.invariant_id)
|
|
113
|
+
].filter(Boolean));
|
|
76
114
|
for (const [index, mapping] of mappings.entries()) {
|
|
77
115
|
const path = `downstream_mapping[${index}]`;
|
|
78
|
-
|
|
116
|
+
if (data.schema_version === 3) {
|
|
117
|
+
for (const field of ["mapping_id", "consumer_capability", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
|
|
118
|
+
requireArray(mapping ?? {}, "source_refs", path, errors, 1);
|
|
119
|
+
requireArray(mapping ?? {}, "evidence_refs", path, errors, 1);
|
|
120
|
+
if (mapping?.consumer_capability && !consumerCapabilities.has(mapping.consumer_capability)) errors.push(`${path}.consumer_capability 非法`);
|
|
121
|
+
for (const reference of mapping?.source_refs ?? []) if (!strategySourceIds.has(reference)) errors.push(`${path}.source_refs 未在领域战略中声明: ${reference}`);
|
|
122
|
+
} else for (const field of ["domain_change", "consumer", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
|
|
79
123
|
if (mapping?.propagation && !["direct", "transitive", "not-applicable", "stale"].includes(mapping.propagation)) errors.push(`${path}.propagation 非法`);
|
|
80
124
|
}
|
|
81
125
|
const approval = data.approval ?? {};
|
package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml
CHANGED
|
@@ -1,16 +1,41 @@
|
|
|
1
|
-
schema_version:
|
|
1
|
+
schema_version: 3
|
|
2
2
|
stage_decision_id: stage-decision.supplier-admission
|
|
3
|
-
package_version:
|
|
3
|
+
package_version: v3
|
|
4
4
|
status: approved
|
|
5
5
|
problem_statement: 合规准入决定分散,采购无法稳定判断当前有效资格。
|
|
6
6
|
target_users: [采购专员、合规专员]
|
|
7
7
|
mvp: [提交供应商申请、合规准入决定、采购侧有效性校验]
|
|
8
8
|
non_goals: [供应商结算、采购付款、通用工作流平台]
|
|
9
|
-
success_criteria:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
success_criteria:
|
|
10
|
+
- id: success-criterion.current-admission
|
|
11
|
+
statement: 新采购订单 100% 校验当前有效准入决定。
|
|
12
|
+
source_refs: [rule.current-admission-required]
|
|
13
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
14
|
+
status: confirmed
|
|
15
|
+
test_seams:
|
|
16
|
+
- id: test-seam.admission-validity-check
|
|
17
|
+
statement: admission-validity-check
|
|
18
|
+
source_refs: [scenario.admission-expires, invariant.current-admission-required]
|
|
19
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
20
|
+
status: confirmed
|
|
21
|
+
confirmed_decisions:
|
|
22
|
+
- id: decision.compliance-owns-admission-rules
|
|
23
|
+
statement: ComplianceReview 拥有准入规则语义,ProcurementExecution 只消费决定。
|
|
24
|
+
source_refs: [rule.current-admission-required]
|
|
25
|
+
evidence_refs: [evidence.domain-strategy-review.supplier]
|
|
26
|
+
status: confirmed
|
|
27
|
+
assumptions:
|
|
28
|
+
- id: assumption.unique-subject-ref
|
|
29
|
+
statement: 供应商已有唯一 subject_ref。
|
|
30
|
+
source_refs: [scenario.supplier-submits]
|
|
31
|
+
evidence_refs: [evidence.discovery.supplier]
|
|
32
|
+
status: confirmed
|
|
33
|
+
constraints:
|
|
34
|
+
- id: constraint.preserve-procurement-flow
|
|
35
|
+
statement: 保留现有采购订单流程。
|
|
36
|
+
source_refs: [scenario.admission-expires]
|
|
37
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
38
|
+
status: confirmed
|
|
14
39
|
unresolved_items:
|
|
15
40
|
- item: 供应商历史资料迁移范围
|
|
16
41
|
type: deferred
|
|
@@ -25,7 +50,7 @@ context_snapshot:
|
|
|
25
50
|
term_refs: [Global/Supplier, ComplianceReview/AdmissionDecision]
|
|
26
51
|
domain_strategy_ref:
|
|
27
52
|
domain_strategy_id: domain-strategy.supplier-admission
|
|
28
|
-
domain_version:
|
|
53
|
+
domain_version: v3
|
|
29
54
|
digest: <domain-strategy-digest>
|
|
30
55
|
status: approved
|
|
31
56
|
persisted_ref: domain-strategy.yaml
|
|
@@ -38,17 +63,21 @@ impact_assessment:
|
|
|
38
63
|
cross_repo: false
|
|
39
64
|
high_risk: false
|
|
40
65
|
downstream_mapping:
|
|
41
|
-
-
|
|
42
|
-
|
|
66
|
+
- mapping_id: mapping.backend-admission-validity
|
|
67
|
+
source_refs: [rule.current-admission-required, invariant.current-admission-required]
|
|
68
|
+
consumer_capability: backend-technical-design
|
|
43
69
|
propagation: direct
|
|
44
70
|
reapproval_condition: 领域策略版本或有效性规则变化
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
evidence_refs: [evidence.domain-strategy-review.supplier]
|
|
72
|
+
- mapping_id: mapping.frontend-admission-validity
|
|
73
|
+
source_refs: [scenario.admission-expires]
|
|
74
|
+
consumer_capability: frontend-engineering-design
|
|
47
75
|
propagation: transitive
|
|
48
76
|
reapproval_condition: Spec 基线变更
|
|
77
|
+
evidence_refs: [evidence.domain-strategy-review.supplier]
|
|
49
78
|
evidence_refs: [evidence.discovery.supplier, evidence.domain-strategy-review.supplier]
|
|
50
79
|
approval:
|
|
51
80
|
approval_ref: .agents/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml
|
|
52
81
|
approver: role.product-manager
|
|
53
82
|
persisted_ref: docs/.scratch/supplier/stage-decision/stage-decision-package-v1.yaml
|
|
54
|
-
current_version:
|
|
83
|
+
current_version: v3
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
schema_version:
|
|
1
|
+
schema_version: 3
|
|
2
2
|
domain_strategy_id: domain-strategy.supplier-admission
|
|
3
|
-
domain_version:
|
|
3
|
+
domain_version: v3
|
|
4
4
|
status: approved
|
|
5
|
+
traceability_version: 1
|
|
6
|
+
rule_catalog:
|
|
7
|
+
- rule_id: rule.application-material-complete
|
|
8
|
+
statement: 申请材料必须完整
|
|
9
|
+
responsible_context: SupplierManagement
|
|
10
|
+
status: confirmed
|
|
11
|
+
evidence_refs: [evidence.discovery.supplier]
|
|
12
|
+
- rule_id: rule.current-admission-required
|
|
13
|
+
statement: 新采购订单必须引用当前有效的准入决定。
|
|
14
|
+
responsible_context: ComplianceReview
|
|
15
|
+
status: confirmed
|
|
16
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
5
17
|
contexts:
|
|
6
18
|
- context_id: SupplierManagement
|
|
7
19
|
name: 供应商管理
|
|
@@ -64,6 +76,10 @@ scenarios:
|
|
|
64
76
|
preconditions: [供应商资料可编辑]
|
|
65
77
|
commands: [SubmitSupplierApplication]
|
|
66
78
|
rules: [申请材料必须完整]
|
|
79
|
+
rule_refs: [rule.application-material-complete]
|
|
80
|
+
critical: true
|
|
81
|
+
success_results: [完整申请进入合规审查]
|
|
82
|
+
evidence_refs: [evidence.discovery.supplier]
|
|
67
83
|
events: [SupplierApplicationSubmitted]
|
|
68
84
|
failure_results: [材料不完整时拒绝提交]
|
|
69
85
|
responsible_context: SupplierManagement
|
|
@@ -73,7 +89,11 @@ scenarios:
|
|
|
73
89
|
actor: compliance-officer
|
|
74
90
|
preconditions: [批准决定存在]
|
|
75
91
|
commands: [EvaluateAdmissionValidity]
|
|
76
|
-
rules: [
|
|
92
|
+
rules: [新采购订单必须引用当前有效的准入决定。]
|
|
93
|
+
rule_refs: [rule.current-admission-required]
|
|
94
|
+
critical: true
|
|
95
|
+
success_results: [过期准入决定不能支持新采购订单]
|
|
96
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
77
97
|
events: [SupplierAdmissionExpired]
|
|
78
98
|
failure_results: [采购下单时资格校验失败]
|
|
79
99
|
responsible_context: ComplianceReview
|
|
@@ -94,6 +114,8 @@ invariants:
|
|
|
94
114
|
responsible_context: ComplianceReview
|
|
95
115
|
scenario_refs: [scenario.admission-expires]
|
|
96
116
|
verification_method: 领域场景测试和事件版本校验
|
|
117
|
+
rule_ref: rule.current-admission-required
|
|
118
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
97
119
|
context_snapshot:
|
|
98
120
|
context_ref: CONTEXT.md
|
|
99
121
|
context_schema_version: 1
|
|
@@ -101,13 +123,23 @@ context_snapshot:
|
|
|
101
123
|
referenced_terms_digest: <referenced-terms-digest>
|
|
102
124
|
term_refs: [Global/Supplier, ComplianceReview/AdmissionDecision]
|
|
103
125
|
downstream_mapping:
|
|
104
|
-
-
|
|
126
|
+
- mapping_id: mapping.backend-admission-validity
|
|
127
|
+
source_refs: [rule.current-admission-required, invariant.current-admission-required]
|
|
128
|
+
consumer_capability: backend-technical-design
|
|
105
129
|
impacts: [spec, openapi, procurement-acceptance-tests]
|
|
106
130
|
propagation: stale
|
|
107
131
|
reapproval_condition: ComplianceReview 决定和有效期重新批准
|
|
132
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
133
|
+
- mapping_id: mapping.frontend-admission-validity
|
|
134
|
+
source_refs: [scenario.admission-expires]
|
|
135
|
+
consumer_capability: frontend-engineering-design
|
|
136
|
+
impacts: [ui, frontend]
|
|
137
|
+
propagation: direct
|
|
138
|
+
reapproval_condition: 场景结果或视觉基线变化
|
|
139
|
+
evidence_refs: [evidence.discovery.compliance]
|
|
108
140
|
evidence_refs: [evidence.discovery.supplier, evidence.discovery.compliance]
|
|
109
141
|
approval:
|
|
110
142
|
approval_ref: .agents/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml
|
|
111
143
|
approver: role.product-manager
|
|
112
144
|
persisted_ref: docs/.scratch/supplier/domain/domain-strategy-v1.yaml
|
|
113
|
-
current_version:
|
|
145
|
+
current_version: v3
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { buildDecisionFixture } from "../../../../scripts/fixtures/user-decision/build-fixture.mjs";
|
|
3
2
|
import { createHash } from "node:crypto";
|
|
4
3
|
import { spawnSync } from "node:child_process";
|
|
5
4
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
@@ -14,6 +13,7 @@ const validator = join(testsRoot, "..", "scripts", "validate-domain-strategy.mjs
|
|
|
14
13
|
const packageValidator = join(testsRoot, "..", "scripts", "validate-stage-decision-package.mjs");
|
|
15
14
|
const contextValidator = join(projectRoot, "scripts", "verify-context-contract");
|
|
16
15
|
const migrationTool = join(testsRoot, "..", "scripts", "migrate-context-references.mjs");
|
|
16
|
+
const v3MigrationTool = join(testsRoot, "..", "scripts", "migrate-contract-v3.mjs");
|
|
17
17
|
const validTemplate = join(testsRoot, "fixtures", "valid-supplier-domain.yaml");
|
|
18
18
|
const validPackageTemplate = join(testsRoot, "fixtures", "valid-stage-decision-package.yaml");
|
|
19
19
|
|
|
@@ -29,6 +29,58 @@ function digestYaml(source) {
|
|
|
29
29
|
return `sha256:${createHash("sha256").update(JSON.stringify(canonical(document.toJS({ maxAliasCount: 0 })))).digest("hex")}`;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function parseYaml(source) {
|
|
33
|
+
const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
|
|
34
|
+
if (document.errors.length) throw new Error(document.errors[0].message);
|
|
35
|
+
return document.toJS({ maxAliasCount: 0 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toV2Domain(source) {
|
|
39
|
+
const value = structuredClone(parseYaml(source));
|
|
40
|
+
value.schema_version = 2;
|
|
41
|
+
value.domain_version = "v2";
|
|
42
|
+
delete value.traceability_version;
|
|
43
|
+
delete value.rule_catalog;
|
|
44
|
+
for (const scenario of value.scenarios) {
|
|
45
|
+
delete scenario.rule_refs;
|
|
46
|
+
delete scenario.critical;
|
|
47
|
+
delete scenario.success_results;
|
|
48
|
+
delete scenario.evidence_refs;
|
|
49
|
+
}
|
|
50
|
+
for (const invariant of value.invariants) {
|
|
51
|
+
delete invariant.rule_ref;
|
|
52
|
+
delete invariant.evidence_refs;
|
|
53
|
+
}
|
|
54
|
+
value.downstream_mapping = [{
|
|
55
|
+
domain_change: "admission-decision-validity",
|
|
56
|
+
impacts: ["spec", "openapi", "procurement-acceptance-tests"],
|
|
57
|
+
propagation: "stale",
|
|
58
|
+
reapproval_condition: "ComplianceReview 决定和有效期重新批准"
|
|
59
|
+
}];
|
|
60
|
+
value.approval.current_version = "v2";
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toV2Stage(source, domainDigest) {
|
|
65
|
+
const value = structuredClone(parseYaml(source));
|
|
66
|
+
value.schema_version = 2;
|
|
67
|
+
value.package_version = "v2";
|
|
68
|
+
for (const field of ["success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints"]) {
|
|
69
|
+
value[field] = value[field].map((item) => item.statement);
|
|
70
|
+
}
|
|
71
|
+
value.domain_strategy_ref.domain_version = "v2";
|
|
72
|
+
value.domain_strategy_ref.digest = domainDigest;
|
|
73
|
+
value.domain_strategy_ref.persisted_ref = "domain-strategy-v2.yaml";
|
|
74
|
+
value.downstream_mapping = [{
|
|
75
|
+
domain_change: "admission-decision-validity",
|
|
76
|
+
consumer: "spec-synthesis",
|
|
77
|
+
propagation: "direct",
|
|
78
|
+
reapproval_condition: "领域策略版本或有效性规则变化"
|
|
79
|
+
}];
|
|
80
|
+
value.approval.current_version = "v2";
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
32
84
|
function run(command, file, contextRoot) {
|
|
33
85
|
return spawnSync(process.execPath, [command, file, "--root", contextRoot], { cwd: projectRoot, encoding: "utf8" });
|
|
34
86
|
}
|
|
@@ -56,34 +108,59 @@ context_schema_version: 1
|
|
|
56
108
|
| 准入决定 | 合规审查形成的准入结论。 | AdmissionDecision | ComplianceReview | 避免:\`审批结果\` |
|
|
57
109
|
`;
|
|
58
110
|
|
|
59
|
-
const temporaryRoot = await mkdtemp(join(tmpdir(), "yss-stage-decision-
|
|
111
|
+
const temporaryRoot = await mkdtemp(join(tmpdir(), "yss-stage-decision-v3-"));
|
|
60
112
|
try {
|
|
61
113
|
await writeFile(join(temporaryRoot, "CONTEXT.md"), contextSource);
|
|
62
114
|
const contextResult = spawnSync(process.execPath, [contextValidator, "--root", temporaryRoot, "--allowed-context", "SupplierManagement", "--allowed-context", "ComplianceReview", "--allowed-context", "ProcurementExecution", "--term-ref", "Global/Supplier", "--term-ref", "ComplianceReview/AdmissionDecision", "--json"], { cwd: projectRoot, encoding: "utf8" });
|
|
63
115
|
if (contextResult.status !== 0) throw new Error(`context fixture should pass: ${contextResult.stderr}`);
|
|
64
116
|
const context = JSON.parse(contextResult.stdout);
|
|
65
117
|
|
|
66
|
-
const domainApprovalRef = join(temporaryRoot, "domain-approval.json");
|
|
67
118
|
const domainSource = (await readFile(validTemplate, "utf8"))
|
|
68
|
-
.replace(".agents/skills/yss-stage-decision/tests/fixtures/domain-strategy-approval.yaml", domainApprovalRef)
|
|
69
119
|
.replace("<document-digest>", context.document_digest)
|
|
70
120
|
.replace("<referenced-terms-digest>", context.referenced_terms_digest);
|
|
71
121
|
const domainFile = join(temporaryRoot, "domain-strategy.yaml");
|
|
72
122
|
await writeFile(domainFile, domainSource);
|
|
73
|
-
const domainDecision = buildDecisionFixture(join(temporaryRoot, "domain-decision"), { boundary: "check.domain-strategy-approved", subjectRef: domainFile });
|
|
74
|
-
const domainApproval = parseDocument(await readFile(join(testsRoot, "fixtures/domain-strategy-approval.yaml"), "utf8")).toJS();
|
|
75
|
-
await writeFile(domainApprovalRef, JSON.stringify({ ...domainApproval, subject_ref: domainFile, approval_scope: domainDecision.requirement.scope, user_decision_ref: domainDecision.ref }));
|
|
76
123
|
const pass = run(validator, domainFile, temporaryRoot);
|
|
77
|
-
if (pass.status !== 0) throw new Error(`valid
|
|
124
|
+
if (pass.status !== 0) throw new Error(`valid v3 fixture should pass: ${pass.stderr}`);
|
|
125
|
+
|
|
126
|
+
const v2Domain = toV2Domain(domainSource);
|
|
127
|
+
const v2DomainSource = JSON.stringify(v2Domain, null, 2);
|
|
128
|
+
const v2DomainFile = join(temporaryRoot, "domain-strategy-v2.yaml");
|
|
129
|
+
await writeFile(v2DomainFile, v2DomainSource);
|
|
130
|
+
const v2Pass = run(validator, v2DomainFile, temporaryRoot);
|
|
131
|
+
if (v2Pass.status !== 0) throw new Error(`read-only v2 fixture should pass: ${v2Pass.stderr}`);
|
|
132
|
+
|
|
133
|
+
const migratedDomainV3 = spawnSync(process.execPath, [v3MigrationTool, v2DomainFile, "--kind", "domain-strategy", "--root", temporaryRoot], { cwd: projectRoot, encoding: "utf8" });
|
|
134
|
+
if (migratedDomainV3.status !== 0) throw new Error(`v2 domain strategy should migrate: ${migratedDomainV3.stderr}`);
|
|
135
|
+
const migratedDomainV3Value = JSON.parse(migratedDomainV3.stdout);
|
|
136
|
+
if (migratedDomainV3Value.schema_version !== 3 || migratedDomainV3Value.status !== "draft" || migratedDomainV3Value.approval.approval_ref !== "migration-required") throw new Error("domain v2 -> v3 migration must reset approval and status");
|
|
137
|
+
|
|
138
|
+
const duplicateRule = join(temporaryRoot, "duplicate-rule.yaml");
|
|
139
|
+
const duplicateRuleValue = parseYaml(domainSource);
|
|
140
|
+
duplicateRuleValue.rule_catalog.push(structuredClone(duplicateRuleValue.rule_catalog[0]));
|
|
141
|
+
await writeFile(duplicateRule, JSON.stringify(duplicateRuleValue, null, 2));
|
|
142
|
+
expectBlocked(run(validator, duplicateRule, temporaryRoot), /rule_id.*重复|重复.*rule_id/, "duplicate v3 rule id");
|
|
143
|
+
|
|
144
|
+
const danglingRule = join(temporaryRoot, "dangling-rule.yaml");
|
|
145
|
+
const danglingRuleValue = parseYaml(domainSource);
|
|
146
|
+
danglingRuleValue.scenarios[0].rule_refs = ["rule.missing"];
|
|
147
|
+
await writeFile(danglingRule, JSON.stringify(danglingRuleValue, null, 2));
|
|
148
|
+
expectBlocked(run(validator, danglingRule, temporaryRoot), /rule_refs.*未声明|悬空/, "dangling v3 rule ref");
|
|
149
|
+
|
|
150
|
+
const missingEvidence = join(temporaryRoot, "missing-rule-evidence.yaml");
|
|
151
|
+
const missingEvidenceValue = parseYaml(domainSource);
|
|
152
|
+
missingEvidenceValue.rule_catalog[0].evidence_refs = [];
|
|
153
|
+
await writeFile(missingEvidence, JSON.stringify(missingEvidenceValue, null, 2));
|
|
154
|
+
expectBlocked(run(validator, missingEvidence, temporaryRoot), /evidence_refs/, "missing v3 evidence");
|
|
78
155
|
|
|
79
156
|
const legacyFile = join(temporaryRoot, "legacy-domain.yaml");
|
|
80
|
-
await writeFile(legacyFile,
|
|
157
|
+
await writeFile(legacyFile, JSON.stringify({ ...v2Domain, schema_version: 1 }, null, 2));
|
|
81
158
|
expectBlocked(run(validator, legacyFile, temporaryRoot), /migration-required/, "legacy domain contract");
|
|
82
159
|
|
|
83
160
|
const migratableLegacy = join(temporaryRoot, "migratable-legacy-domain.yaml");
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
161
|
+
const migratableLegacyValue = { ...structuredClone(v2Domain), schema_version: 1, terminology_refs: ["CONTEXT.md#Supplier", "contexts/ComplianceReview/CONTEXT.md#AdmissionDecision"] };
|
|
162
|
+
delete migratableLegacyValue.context_snapshot;
|
|
163
|
+
const migratableLegacySource = JSON.stringify(migratableLegacyValue, null, 2);
|
|
87
164
|
await writeFile(migratableLegacy, migratableLegacySource);
|
|
88
165
|
const migrated = spawnSync(process.execPath, [migrationTool, migratableLegacy, "--root", temporaryRoot], { cwd: projectRoot, encoding: "utf8" });
|
|
89
166
|
if (migrated.status !== 0) throw new Error(`migratable v1 contract should migrate: ${migrated.stderr}`);
|
|
@@ -91,7 +168,7 @@ try {
|
|
|
91
168
|
if (migratedValue.schema_version !== 2 || migratedValue.terminology_refs !== undefined || migratedValue.context_snapshot.context_ref !== "CONTEXT.md" || migratedValue.context_snapshot.term_refs.join(",") !== "ComplianceReview/AdmissionDecision,Global/Supplier") throw new Error("migrated context snapshot is incomplete");
|
|
92
169
|
|
|
93
170
|
const ambiguousLegacy = join(temporaryRoot, "ambiguous-legacy-domain.yaml");
|
|
94
|
-
await writeFile(ambiguousLegacy,
|
|
171
|
+
await writeFile(ambiguousLegacy, JSON.stringify({ ...migratableLegacyValue, terminology_refs: ["contexts/ComplianceReview/CONTEXT.md"] }, null, 2));
|
|
95
172
|
const ambiguousMigration = spawnSync(process.execPath, [migrationTool, ambiguousLegacy, "--root", temporaryRoot], { cwd: projectRoot, encoding: "utf8" });
|
|
96
173
|
expectBlocked(ambiguousMigration, /migration-required.*无法唯一定位|无法唯一定位.*migration-required/, "ambiguous legacy reference");
|
|
97
174
|
|
|
@@ -115,32 +192,48 @@ try {
|
|
|
115
192
|
await writeFile(unknownContext, domainSource.replace("to_context: ComplianceReview", "to_context: UnknownContext"));
|
|
116
193
|
expectBlocked(run(validator, unknownContext, temporaryRoot), /未引用已声明上下文|未在领域战略中登记/, "unknown context");
|
|
117
194
|
|
|
118
|
-
const packageApprovalRef = join(temporaryRoot, "package-approval.json");
|
|
119
195
|
const packageSource = (await readFile(validPackageTemplate, "utf8"))
|
|
120
|
-
.replace(".agents/skills/yss-stage-decision/tests/fixtures/stage-decision-approval.yaml", packageApprovalRef)
|
|
121
196
|
.replace("<document-digest>", context.document_digest)
|
|
122
197
|
.replace("<referenced-terms-digest>", context.referenced_terms_digest)
|
|
123
198
|
.replace("<domain-strategy-digest>", digestYaml(domainSource));
|
|
124
199
|
const packageFile = join(temporaryRoot, "stage-decision.yaml");
|
|
125
200
|
await writeFile(packageFile, packageSource);
|
|
126
|
-
const packageDecision = buildDecisionFixture(join(temporaryRoot, "package-decision"), { boundary: "check.stage-decision-package-approved", subjectRef: packageFile });
|
|
127
|
-
const packageApproval = parseDocument(await readFile(join(testsRoot, "fixtures/stage-decision-approval.yaml"), "utf8")).toJS();
|
|
128
|
-
await writeFile(packageApprovalRef, JSON.stringify({ ...packageApproval, subject_ref: packageFile, approval_scope: packageDecision.requirement.scope, user_decision_ref: packageDecision.ref }));
|
|
129
201
|
const packagePass = run(packageValidator, packageFile, temporaryRoot);
|
|
130
|
-
if (packagePass.status !== 0) throw new Error(`valid
|
|
202
|
+
if (packagePass.status !== 0) throw new Error(`valid v3 stage decision package should pass: ${packagePass.stderr}`);
|
|
203
|
+
|
|
204
|
+
const v2Stage = toV2Stage(packageSource, digestYaml(v2DomainSource));
|
|
205
|
+
const v2StageFile = join(temporaryRoot, "stage-decision-v2.yaml");
|
|
206
|
+
await writeFile(v2StageFile, JSON.stringify(v2Stage, null, 2));
|
|
207
|
+
const v2StagePass = run(packageValidator, v2StageFile, temporaryRoot);
|
|
208
|
+
if (v2StagePass.status !== 0) throw new Error(`read-only v2 stage decision package should pass: ${v2StagePass.stderr}`);
|
|
209
|
+
|
|
210
|
+
const migratedV3 = spawnSync(process.execPath, [v3MigrationTool, v2StageFile, "--kind", "stage-decision", "--root", temporaryRoot], { cwd: projectRoot, encoding: "utf8" });
|
|
211
|
+
if (migratedV3.status !== 0) throw new Error(`v2 stage decision should migrate: ${migratedV3.stderr}`);
|
|
212
|
+
const migratedV3Value = JSON.parse(migratedV3.stdout);
|
|
213
|
+
if (migratedV3Value.schema_version !== 3 || migratedV3Value.status !== "draft" || migratedV3Value.approval.approval_ref !== "migration-required") throw new Error("v2 -> v3 migration must reset approval and status");
|
|
214
|
+
|
|
215
|
+
const wrongSourceRef = join(temporaryRoot, "stage-decision-wrong-source-ref.yaml");
|
|
216
|
+
const wrongSourceRefValue = parseYaml(packageSource);
|
|
217
|
+
wrongSourceRefValue.success_criteria[0].source_refs = ["rule.missing"];
|
|
218
|
+
await writeFile(wrongSourceRef, JSON.stringify(wrongSourceRefValue, null, 2));
|
|
219
|
+
expectBlocked(run(packageValidator, wrongSourceRef, temporaryRoot), /source_refs.*未在领域战略中声明|悬空/, "stage decision dangling source ref");
|
|
131
220
|
|
|
132
221
|
const packageLegacy = join(temporaryRoot, "legacy-stage-decision.yaml");
|
|
133
|
-
await writeFile(packageLegacy,
|
|
222
|
+
await writeFile(packageLegacy, JSON.stringify({ ...v2Stage, schema_version: 1 }, null, 2));
|
|
134
223
|
expectBlocked(run(packageValidator, packageLegacy, temporaryRoot), /migration-required/, "legacy stage decision contract");
|
|
135
224
|
|
|
136
225
|
const packageBlocker = join(temporaryRoot, "stage-decision-blocker.yaml");
|
|
137
|
-
|
|
226
|
+
const blockerValue = parseYaml(packageSource);
|
|
227
|
+
blockerValue.unresolved_items[0].type = "blocker";
|
|
228
|
+
await writeFile(packageBlocker, JSON.stringify(blockerValue, null, 2));
|
|
138
229
|
expectBlocked(run(packageValidator, packageBlocker, temporaryRoot), /blocker/, "blocker package");
|
|
139
230
|
|
|
140
231
|
const packageWrongArray = join(temporaryRoot, "stage-decision-wrong-array.yaml");
|
|
141
|
-
|
|
232
|
+
const wrongArrayValue = parseYaml(packageSource);
|
|
233
|
+
wrongArrayValue.target_users = [42];
|
|
234
|
+
await writeFile(packageWrongArray, JSON.stringify(wrongArrayValue, null, 2));
|
|
142
235
|
expectBlocked(run(packageValidator, packageWrongArray, temporaryRoot), /target_users\[0\]/, "package non-string array item");
|
|
143
236
|
} finally {
|
|
144
237
|
await rm(temporaryRoot, { recursive: true, force: true });
|
|
145
238
|
}
|
|
146
|
-
process.stdout.write("业务边界与规则设计 v2 场景验证通过\n");
|
|
239
|
+
process.stdout.write("业务边界与规则设计 v2/v3 场景验证通过\n");
|
|
@@ -24,6 +24,8 @@ description: 在 YSS 技术分析中统一承接需求与战略输入,按已
|
|
|
24
24
|
|
|
25
25
|
已有 Strategic Design Handoff 时,两种分支都必须先完成导入和目标词汇对账,再逐条承接规则与关键场景。没有战略交接包的项目直接消费批准需求,不能为 MVC 补造战略 DDD。
|
|
26
26
|
|
|
27
|
+
既有身份 v2 的纯工程变更可显式使用 `design_scope: engineering-only`;按 `references/engineering-design.schema.json` 绑定真实构建单元、写路径、只读依赖、失败处理与测试 seam。不得更改登记业务边界、领域模型、业务规则/状态、持久化、数据库 schema 或 UI;命中这些影响回原架构设计。缺省仍保留原 DDD/MVC 规则,不自动降级。工程分支仍需当前 Spec/API/工程依据、逐条承接、独立技术审查及正式 Slice 批准。
|
|
28
|
+
|
|
27
29
|
## 合同与验证
|
|
28
30
|
|
|
29
31
|
新产物使用 schema v2 Technical Design Contract:共同头记录架构来源、输入版本与摘要、规则/场景承接、评审证据和状态,`design` 保存架构专属内容。合同格式与兼容规则见 `references/technical-design-contract.md`,Schema 见 `references/technical-design.schema.json`。
|