create-yss-spec 3.3.4 → 3.3.7

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.
Files changed (171) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/src/template/instance-runtime.js +8 -2
  4. package/src/validation/snapshot.js +7 -0
  5. package/template/.agents/skills/.strategic-design-skills-manifest.json +7 -5
  6. package/template/.agents/skills/archify/SKILL.md +1 -1
  7. package/template/.agents/skills/code-review/SKILL.md +6 -0
  8. package/template/.agents/skills/i-have-adhd/LICENSE +21 -0
  9. package/template/.agents/skills/i-have-adhd/SKILL.md +26 -0
  10. package/template/.agents/skills/i-have-adhd/agents/openai.yaml +6 -0
  11. package/template/.agents/skills/i-have-adhd/references/yss-adaptation.md +21 -0
  12. package/template/.agents/skills/i-have-adhd/tests/verify-integration.mjs +63 -0
  13. package/template/.agents/skills/prototype-review/SKILL.md +2 -0
  14. package/template/.agents/skills/yss-implementation-contract-compiler/SKILL.md +6 -0
  15. package/template/.agents/skills/yss-implementation-contract-compiler/references/boundaries.md +10 -0
  16. package/template/.agents/skills/yss-openapi-governance/SKILL.md +6 -0
  17. package/template/.agents/skills/yss-product-lifecycle/SKILL.md +3 -1
  18. package/template/.agents/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -0
  19. package/template/.agents/skills/yss-prototype-stage/SKILL.md +6 -0
  20. package/template/.agents/skills/yss-research/SKILL.md +6 -0
  21. package/template/.agents/skills/yss-stage-decision/SKILL.md +19 -4
  22. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  23. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  24. package/template/.agents/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  25. package/template/.agents/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  26. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  27. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  28. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  29. package/template/.agents/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  30. package/template/.agents/skills/yss-stage-decision/references/validation-rules.md +2 -2
  31. package/template/.agents/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  32. package/template/.agents/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  33. package/template/.agents/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  34. package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  35. package/template/.agents/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  36. package/template/.agents/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  37. package/template/.agents/skills/yss-technical-design/SKILL.md +6 -0
  38. package/template/.codex/skills/archify/SKILL.md +1 -1
  39. package/template/.codex/skills/code-review/SKILL.md +6 -0
  40. package/template/.codex/skills/i-have-adhd/LICENSE +21 -0
  41. package/template/.codex/skills/i-have-adhd/SKILL.md +26 -0
  42. package/template/.codex/skills/i-have-adhd/agents/openai.yaml +6 -0
  43. package/template/.codex/skills/i-have-adhd/references/yss-adaptation.md +21 -0
  44. package/template/.codex/skills/i-have-adhd/tests/verify-integration.mjs +63 -0
  45. package/template/.codex/skills/prototype-review/SKILL.md +2 -0
  46. package/template/.codex/skills/yss-implementation-contract-compiler/SKILL.md +6 -0
  47. package/template/.codex/skills/yss-implementation-contract-compiler/references/boundaries.md +10 -0
  48. package/template/.codex/skills/yss-openapi-governance/SKILL.md +6 -0
  49. package/template/.codex/skills/yss-product-lifecycle/SKILL.md +3 -1
  50. package/template/.codex/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -0
  51. package/template/.codex/skills/yss-prototype-stage/SKILL.md +6 -0
  52. package/template/.codex/skills/yss-research/SKILL.md +6 -0
  53. package/template/.codex/skills/yss-stage-decision/SKILL.md +19 -4
  54. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  55. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  56. package/template/.codex/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  57. package/template/.codex/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  58. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  59. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  60. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  61. package/template/.codex/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  62. package/template/.codex/skills/yss-stage-decision/references/validation-rules.md +2 -2
  63. package/template/.codex/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  64. package/template/.codex/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  65. package/template/.codex/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  66. package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  67. package/template/.codex/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  68. package/template/.codex/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  69. package/template/.codex/skills/yss-technical-design/SKILL.md +6 -0
  70. package/template/.cursor/skills/archify/SKILL.md +1 -1
  71. package/template/.cursor/skills/code-review/SKILL.md +6 -0
  72. package/template/.cursor/skills/i-have-adhd/LICENSE +21 -0
  73. package/template/.cursor/skills/i-have-adhd/SKILL.md +26 -0
  74. package/template/.cursor/skills/i-have-adhd/agents/openai.yaml +6 -0
  75. package/template/.cursor/skills/i-have-adhd/references/yss-adaptation.md +21 -0
  76. package/template/.cursor/skills/i-have-adhd/tests/verify-integration.mjs +63 -0
  77. package/template/.cursor/skills/prototype-review/SKILL.md +2 -0
  78. package/template/.cursor/skills/yss-implementation-contract-compiler/SKILL.md +6 -0
  79. package/template/.cursor/skills/yss-implementation-contract-compiler/references/boundaries.md +10 -0
  80. package/template/.cursor/skills/yss-openapi-governance/SKILL.md +6 -0
  81. package/template/.cursor/skills/yss-product-lifecycle/SKILL.md +3 -1
  82. package/template/.cursor/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -0
  83. package/template/.cursor/skills/yss-prototype-stage/SKILL.md +6 -0
  84. package/template/.cursor/skills/yss-research/SKILL.md +6 -0
  85. package/template/.cursor/skills/yss-stage-decision/SKILL.md +19 -4
  86. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  87. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  88. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  89. package/template/.cursor/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  90. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  91. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  92. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  93. package/template/.cursor/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  94. package/template/.cursor/skills/yss-stage-decision/references/validation-rules.md +2 -2
  95. package/template/.cursor/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  96. package/template/.cursor/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  97. package/template/.cursor/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  98. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  99. package/template/.cursor/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  100. package/template/.cursor/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  101. package/template/.cursor/skills/yss-technical-design/SKILL.md +6 -0
  102. package/template/.pi/skills/archify/SKILL.md +1 -1
  103. package/template/.pi/skills/code-review/SKILL.md +6 -0
  104. package/template/.pi/skills/i-have-adhd/LICENSE +21 -0
  105. package/template/.pi/skills/i-have-adhd/SKILL.md +26 -0
  106. package/template/.pi/skills/i-have-adhd/agents/openai.yaml +6 -0
  107. package/template/.pi/skills/i-have-adhd/references/yss-adaptation.md +21 -0
  108. package/template/.pi/skills/i-have-adhd/tests/verify-integration.mjs +63 -0
  109. package/template/.pi/skills/prototype-review/SKILL.md +2 -0
  110. package/template/.pi/skills/yss-implementation-contract-compiler/SKILL.md +6 -0
  111. package/template/.pi/skills/yss-implementation-contract-compiler/references/boundaries.md +10 -0
  112. package/template/.pi/skills/yss-openapi-governance/SKILL.md +6 -0
  113. package/template/.pi/skills/yss-product-lifecycle/SKILL.md +3 -1
  114. package/template/.pi/skills/yss-product-lifecycle/references/orchestration-contract.yaml +12 -0
  115. package/template/.pi/skills/yss-prototype-stage/SKILL.md +6 -0
  116. package/template/.pi/skills/yss-research/SKILL.md +6 -0
  117. package/template/.pi/skills/yss-stage-decision/SKILL.md +19 -4
  118. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-contract.md +4 -3
  119. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-v2.schema.json +8 -0
  120. package/template/.pi/skills/yss-stage-decision/references/domain-strategy-v3.schema.json +8 -0
  121. package/template/.pi/skills/yss-stage-decision/references/domain-strategy.schema.json +75 -5
  122. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-contract.md +4 -4
  123. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-v2.schema.json +8 -0
  124. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package-v3.schema.json +8 -0
  125. package/template/.pi/skills/yss-stage-decision/references/stage-decision-package.schema.json +59 -9
  126. package/template/.pi/skills/yss-stage-decision/references/validation-rules.md +2 -2
  127. package/template/.pi/skills/yss-stage-decision/scripts/migrate-contract-v3.mjs +143 -0
  128. package/template/.pi/skills/yss-stage-decision/scripts/validate-domain-strategy.mjs +46 -4
  129. package/template/.pi/skills/yss-stage-decision/scripts/validate-stage-decision-package.mjs +48 -4
  130. package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-stage-decision-package.yaml +42 -13
  131. package/template/.pi/skills/yss-stage-decision/tests/fixtures/valid-supplier-domain.yaml +37 -5
  132. package/template/.pi/skills/yss-stage-decision/tests/run-scenarios.mjs +116 -23
  133. package/template/.pi/skills/yss-technical-design/SKILL.md +6 -0
  134. package/template/docs/agents/yss-skill-registry.yaml +22 -0
  135. package/template/docs/plan/templates/plan-template.md +8 -4
  136. package/template/docs/process/document-writing.md +77 -0
  137. package/template/docs/process/frontend-backend-delivery.md +7 -4
  138. package/template/docs/process/schemas/backend-delivery.schema.json +4 -0
  139. package/template/docs/process/schemas/frontend-delivery-acceptance-v1.schema.json +200 -0
  140. package/template/docs/process/schemas/frontend-delivery-acceptance.schema.json +37 -3
  141. package/template/docs/process/schemas/frontend-strategic-preflight.schema.json +32 -0
  142. package/template/docs/process/schemas/lifecycle-checkpoint.schema.json +2 -1
  143. package/template/docs/process/schemas/strategic-consumer-feedback.schema.json +40 -0
  144. package/template/docs/process/schemas/strategic-design-handoff-v3.schema.json +743 -0
  145. package/template/docs/process/schemas/strategic-design-handoff-v4.schema.json +712 -0
  146. package/template/docs/process/schemas/strategic-feedback-adjudication.schema.json +42 -0
  147. package/template/docs/process/schemas/strategic-handoff-domain-strategy-v3.schema.json +216 -0
  148. package/template/docs/process/schemas/strategic-handoff-import-receipt.schema.json +39 -0
  149. package/template/docs/process/schemas/strategic-handoff-stage-decision-package-v3.schema.json +148 -0
  150. package/template/docs/process/strategic-handoff-package.md +37 -4
  151. package/template/docs/process/templates/lifecycle-checkpoint-template.yaml +1 -0
  152. package/template/docs/process/templates/stage-checkpoint-template.md +1 -0
  153. package/template/docs/templates/examples/lifecycle-writing-examples.md +138 -0
  154. package/template/docs/templates/spec-template.md +16 -3
  155. package/template/docs/templates/vertical-slice-ticket-template.md +8 -4
  156. package/template/scripts/lib/approval-record.mjs +2 -1
  157. package/template/scripts/lib/backend-delivery.mjs +15 -3
  158. package/template/scripts/lib/frontend-delivery-boundary.mjs +20 -4
  159. package/template/scripts/lib/frontend-delivery.mjs +66 -2
  160. package/template/scripts/lib/profile-skill-sync.mjs +336 -0
  161. package/template/scripts/lib/strategic-feedback.mjs +33 -0
  162. package/template/scripts/lib/strategic-handoff-consumption.mjs +5 -0
  163. package/template/scripts/lib/strategic-handoff-io.mjs +1 -0
  164. package/template/scripts/lib/strategic-handoff.mjs +101 -10
  165. package/template/scripts/lib/user-decision-reuse.mjs +38 -0
  166. package/template/scripts/strategic-feedback +10 -0
  167. package/template/scripts/sync-profile-skills +43 -0
  168. package/template/scripts/verify-frontend-strategic-preflight +12 -0
  169. package/template/skills-lock.json +38 -20
  170. package/template.manifest.json +1 -0
  171. package/template.snapshot.json +6 -5
@@ -2,8 +2,8 @@
2
2
 
3
3
  验证分为结构、引用和语义三层:
4
4
 
5
- 1. **结构**:Schema 版本、稳定 ID、状态枚举、必填字段、条件字段和证据格式。
6
- 2. **引用**:上下文端点、场景、不变量、`context_snapshot.term_refs` 和下游资产引用必须可解析;`context_ref` 必须精确为根 `CONTEXT.md`,双摘要必须与当前内容一致。
5
+ 1. **结构**:Schema 版本、全局稳定 ID 唯一性、状态枚举、必填字段、条件字段和非空证据格式。
6
+ 2. **引用**:上下文端点、规则、场景、不变量、决定、测试 seam、`context_snapshot.term_refs` 和下游 mapping 来源必须可解析;`context_ref` 必须精确为根 `CONTEXT.md`,双摘要必须与当前内容一致。
7
7
  3. **语义**:禁止关系自环、未知上下文、未批准 Shared Kernel、未解释的语义/技术方向差异、职责重叠、无责任人的不变量和跨上下文直接共享实体。
8
8
 
9
9
  以下情况必须 `blocked`:关键规则所有权未知、关键术语冲突、上下文边界未定、关键失败场景缺失、事件语义无法解释、领域专家确认缺失。
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import process from "node:process";
5
+ import { parseArgs } from "node:util";
6
+ import { parseDocument } from "../../../../scripts/vendor/yaml.mjs";
7
+
8
+ function blocked(message) {
9
+ process.stderr.write(`${JSON.stringify({ result: "blocked", reason_code: "migration-required", errors: [message] }, null, 2)}\n`);
10
+ process.exitCode = 1;
11
+ }
12
+
13
+ function parse(source, label) {
14
+ const document = parseDocument(source, { maxAliasCount: 0, uniqueKeys: true });
15
+ if (document.errors.length) throw new TypeError(`${label} YAML 非法: ${document.errors[0].message}`);
16
+ const value = document.toJS({ maxAliasCount: 0 });
17
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError(`${label} 必须是对象`);
18
+ return value;
19
+ }
20
+
21
+ function nextVersion(value) {
22
+ const matched = String(value ?? "v2").match(/^v(\d+)$/);
23
+ return `v${Math.max(3, Number(matched?.[1] ?? 2) + 1)}`;
24
+ }
25
+
26
+ function token(value, fallback) {
27
+ const normalized = String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
28
+ return normalized || fallback;
29
+ }
30
+
31
+ function resetApproval(value, version) {
32
+ value.status = "draft";
33
+ value.approval = {
34
+ approval_ref: "migration-required",
35
+ approver: "unassigned",
36
+ persisted_ref: value.approval?.persisted_ref || "migration-output-not-persisted",
37
+ current_version: version
38
+ };
39
+ }
40
+
41
+ function migrateDomain(input) {
42
+ const output = structuredClone(input);
43
+ if (output.schema_version !== 2) throw new TypeError("Domain Strategy 迁移仅支持 schema_version: 2");
44
+ const evidence = Array.isArray(output.evidence_refs) && output.evidence_refs.length ? output.evidence_refs : ["migration-required:evidence"];
45
+ const rules = [];
46
+ const statementToId = new Map();
47
+ for (const [scenarioIndex, scenario] of (output.scenarios ?? []).entries()) {
48
+ scenario.rule_refs = [];
49
+ for (const [ruleIndex, statement] of (scenario.rules ?? []).entries()) {
50
+ let ruleId = statementToId.get(statement);
51
+ if (!ruleId) {
52
+ ruleId = `rule.${token(scenario.scenario_id?.replace(/^scenario\./, ""), `scenario-${scenarioIndex + 1}`)}-${ruleIndex + 1}`;
53
+ statementToId.set(statement, ruleId);
54
+ rules.push({ rule_id: ruleId, statement, responsible_context: scenario.responsible_context, status: "confirmed", evidence_refs: [...evidence] });
55
+ }
56
+ scenario.rule_refs.push(ruleId);
57
+ }
58
+ scenario.critical = false;
59
+ scenario.success_results = Array.isArray(scenario.events) && scenario.events.length ? [...scenario.events] : ["migration-required: 补充成功结果"];
60
+ scenario.evidence_refs = [...evidence];
61
+ }
62
+ for (const [index, invariant] of (output.invariants ?? []).entries()) {
63
+ let ruleId = statementToId.get(invariant.statement);
64
+ if (!ruleId) {
65
+ ruleId = `rule.${token(invariant.invariant_id?.replace(/^invariant\./, ""), `invariant-${index + 1}`)}`;
66
+ statementToId.set(invariant.statement, ruleId);
67
+ rules.push({ rule_id: ruleId, statement: invariant.statement, responsible_context: invariant.responsible_context, status: "confirmed", evidence_refs: [...evidence] });
68
+ }
69
+ invariant.rule_ref = ruleId;
70
+ invariant.evidence_refs = [...evidence];
71
+ }
72
+ const sourceRefs = [...(output.scenarios ?? []).map((item) => item.scenario_id), ...(output.invariants ?? []).map((item) => item.invariant_id)].filter(Boolean);
73
+ output.downstream_mapping = (output.downstream_mapping ?? []).map((mapping, index) => {
74
+ const impacts = Array.isArray(mapping.impacts) ? mapping.impacts : ["migration-required"];
75
+ const isFrontend = impacts.some((item) => /frontend|ui|visual/i.test(item));
76
+ return {
77
+ mapping_id: `mapping.${token(mapping.domain_change, `migrated-${index + 1}`)}`,
78
+ source_refs: sourceRefs.length ? sourceRefs : [rules[0]?.rule_id].filter(Boolean),
79
+ consumer_capability: isFrontend ? "frontend-engineering-design" : "backend-technical-design",
80
+ impacts,
81
+ propagation: mapping.propagation,
82
+ reapproval_condition: mapping.reapproval_condition,
83
+ evidence_refs: [...evidence]
84
+ };
85
+ });
86
+ output.schema_version = 3;
87
+ output.traceability_version = 1;
88
+ output.rule_catalog = rules;
89
+ output.domain_version = nextVersion(output.domain_version);
90
+ resetApproval(output, output.domain_version);
91
+ return output;
92
+ }
93
+
94
+ async function migrateStage(input, root) {
95
+ const output = structuredClone(input);
96
+ if (output.schema_version !== 2) throw new TypeError("Stage Decision 迁移仅支持 schema_version: 2");
97
+ const evidence = Array.isArray(output.evidence_refs) && output.evidence_refs.length ? output.evidence_refs : ["migration-required:evidence"];
98
+ let sourceRefs = [];
99
+ try {
100
+ const domainSource = await readFile(resolve(root, output.domain_strategy_ref.persisted_ref), "utf8");
101
+ const domain = parse(domainSource, "Domain Strategy");
102
+ sourceRefs = [...(domain.rule_catalog ?? []).map((item) => item.rule_id), ...(domain.scenarios ?? []).map((item) => item.scenario_id), ...(domain.invariants ?? []).map((item) => item.invariant_id)].filter(Boolean);
103
+ } catch {
104
+ sourceRefs = [];
105
+ }
106
+ const prefixes = { success_criteria: "success-criterion", test_seams: "test-seam", confirmed_decisions: "decision", assumptions: "assumption", constraints: "constraint" };
107
+ for (const [field, prefix] of Object.entries(prefixes)) {
108
+ output[field] = (output[field] ?? []).map((statement, index) => ({
109
+ id: `${prefix}.${token(statement, `migrated-${index + 1}`)}`,
110
+ statement,
111
+ source_refs: sourceRefs.slice(0, 1),
112
+ evidence_refs: [...evidence],
113
+ status: "candidate"
114
+ }));
115
+ }
116
+ output.downstream_mapping = (output.downstream_mapping ?? []).map((mapping, index) => ({
117
+ mapping_id: `mapping.${token(mapping.domain_change, `migrated-${index + 1}`)}`,
118
+ source_refs: sourceRefs.slice(0, 1),
119
+ consumer_capability: /frontend|ui/i.test(mapping.consumer ?? "") ? "frontend-engineering-design" : /coord/i.test(mapping.consumer ?? "") ? "delivery-coordination" : "backend-technical-design",
120
+ propagation: mapping.propagation,
121
+ reapproval_condition: mapping.reapproval_condition,
122
+ evidence_refs: [...evidence]
123
+ }));
124
+ output.schema_version = 3;
125
+ output.package_version = nextVersion(output.package_version);
126
+ resetApproval(output, output.package_version);
127
+ return output;
128
+ }
129
+
130
+ try {
131
+ const { values, positionals } = parseArgs({
132
+ options: { kind: { type: "string" }, root: { type: "string", default: process.cwd() } },
133
+ allowPositionals: true,
134
+ strict: true
135
+ });
136
+ const file = positionals[0];
137
+ if (!file || !["domain-strategy", "stage-decision"].includes(values.kind)) throw new TypeError("用法: migrate-contract-v3.mjs <contract.yaml> --kind <domain-strategy|stage-decision> [--root <project-root>]");
138
+ const value = parse(await readFile(file, "utf8"), "合同");
139
+ const migrated = values.kind === "domain-strategy" ? migrateDomain(value) : await migrateStage(value, values.root);
140
+ process.stdout.write(`${JSON.stringify(migrated, null, 2)}\n`);
141
+ } catch (error) {
142
+ blocked(error.message);
143
+ }
@@ -18,6 +18,7 @@ const idPatterns = {
18
18
  concept_id: /^domain-concept\.[a-z0-9][a-z0-9-]*$/,
19
19
  invariant_id: /^invariant\.[a-z0-9][a-z0-9-]*$/
20
20
  };
21
+ const consumerCapabilities = new Set(["backend-technical-design", "frontend-engineering-design", "delivery-coordination"]);
21
22
  const relationshipPatterns = new Set(["Partnership", "Customer/Supplier", "Conformist", "Anti-Corruption Layer", "Open Host Service", "Published Language", "Shared Kernel", "Separate Ways"]);
22
23
 
23
24
  function fail(errors) {
@@ -53,7 +54,7 @@ function validate(data, contextRoot) {
53
54
  if (!data || typeof data !== "object" || Array.isArray(data)) return ["合同必须是对象"];
54
55
  if (data.schema_version === 1) return ["migration-required: domain strategy v1 必须迁移到 v2 context_snapshot"];
55
56
  for (const field of required) requireField(data, field, "root", errors);
56
- if (data.schema_version !== 2) errors.push("schema_version 必须为 2");
57
+ if (![2, 3].includes(data.schema_version)) errors.push("schema_version 不受支持;支持版本: 2, 3;新交付请迁移到 3");
57
58
  if (!idPatterns.domain_strategy_id.test(String(data.domain_strategy_id ?? ""))) errors.push("domain_strategy_id 格式非法");
58
59
  if (!/^v[0-9]+$/.test(String(data.domain_version ?? ""))) errors.push("domain_version 必须形如 v1");
59
60
  if (!["draft", "ready-for-human", "approved", "stale", "blocked"].includes(data.status)) errors.push("status 非法");
@@ -71,6 +72,23 @@ function validate(data, contextRoot) {
71
72
  if (context?.subdomain_type && !["Core Domain", "Supporting Subdomain", "Generic Subdomain"].includes(context.subdomain_type)) errors.push(`${path}.subdomain_type 非法`);
72
73
  }
73
74
 
75
+ const sourceIds = new Set();
76
+ if (data.schema_version === 3) {
77
+ if (data.traceability_version !== 1) errors.push("traceability_version 必须为 1");
78
+ requireArray(data, "rule_catalog", "root", errors, 1, "object");
79
+ const rules = list(data.rule_catalog) ? data.rule_catalog : [];
80
+ unique(rules.map((item) => item?.rule_id).filter(Boolean), "rule_id", errors);
81
+ for (const [index, rule] of rules.entries()) {
82
+ const path = `rule_catalog[${index}]`;
83
+ for (const field of ["rule_id", "statement", "responsible_context", "status"]) requireString(rule ?? {}, field, path, errors);
84
+ requireArray(rule ?? {}, "evidence_refs", path, errors, 1);
85
+ if (!/^rule\.[a-z0-9][a-z0-9-]*$/.test(String(rule?.rule_id ?? ""))) errors.push(`${path}.rule_id 格式非法`);
86
+ if (rule?.responsible_context && !contextIds.includes(rule.responsible_context)) errors.push(`${path}.responsible_context 未声明`);
87
+ if (rule?.status !== "confirmed") errors.push(`${path}.status 必须为 confirmed`);
88
+ if (rule?.rule_id) sourceIds.add(rule.rule_id);
89
+ }
90
+ }
91
+
74
92
  requireArray(data, "subdomains", "root", errors, 1, "object");
75
93
  const subdomains = list(data.subdomains) ? data.subdomains : [];
76
94
  unique(subdomains.map((item) => item?.subdomain_id).filter(Boolean), "subdomain_id", errors);
@@ -110,6 +128,14 @@ function validate(data, contextRoot) {
110
128
  if (scenario?.scenario_id && !idPatterns.scenario_id.test(scenario.scenario_id)) errors.push(`${path}.scenario_id 格式非法`);
111
129
  if (scenario?.responsible_context && !contextIds.includes(scenario.responsible_context)) errors.push(`${path}.responsible_context 未声明`);
112
130
  for (const consumer of scenario?.consumers ?? []) if (!contextIds.includes(consumer)) errors.push(`${path}.consumers 引用了未声明上下文: ${consumer}`);
131
+ if (scenario?.scenario_id) sourceIds.add(scenario.scenario_id);
132
+ if (data.schema_version === 3) {
133
+ requireArray(scenario ?? {}, "rule_refs", path, errors, 1);
134
+ requireArray(scenario ?? {}, "success_results", path, errors, 1);
135
+ requireArray(scenario ?? {}, "evidence_refs", path, errors, 1);
136
+ if (typeof scenario?.critical !== "boolean") errors.push(`${path}.critical 必须是 boolean`);
137
+ for (const reference of scenario?.rule_refs ?? []) if (!sourceIds.has(reference)) errors.push(`${path}.rule_refs 引用了未声明规则: ${reference}`);
138
+ }
113
139
  }
114
140
 
115
141
  const concepts = list(data.concept_candidates) ? data.concept_candidates : [];
@@ -132,6 +158,12 @@ function validate(data, contextRoot) {
132
158
  if (invariant?.invariant_id && !idPatterns.invariant_id.test(invariant.invariant_id)) errors.push(`${path}.invariant_id 格式非法`);
133
159
  if (invariant?.responsible_context && !contextIds.includes(invariant.responsible_context)) errors.push(`${path}.responsible_context 未声明`);
134
160
  for (const reference of invariant?.scenario_refs ?? []) if (!scenarioIds.includes(reference)) errors.push(`${path}.scenario_refs 引用了未声明场景: ${reference}`);
161
+ if (invariant?.invariant_id) sourceIds.add(invariant.invariant_id);
162
+ if (data.schema_version === 3) {
163
+ requireString(invariant ?? {}, "rule_ref", path, errors);
164
+ requireArray(invariant ?? {}, "evidence_refs", path, errors, 1);
165
+ if (invariant?.rule_ref && !sourceIds.has(invariant.rule_ref)) errors.push(`${path}.rule_ref 引用了未声明规则: ${invariant.rule_ref}`);
166
+ }
135
167
  }
136
168
 
137
169
  try {
@@ -141,11 +173,21 @@ function validate(data, contextRoot) {
141
173
  }
142
174
  requireArray(data, "evidence_refs", "root", errors, 1);
143
175
  const mappings = list(data.downstream_mapping) ? data.downstream_mapping : [];
176
+ if (data.schema_version === 3) unique(mappings.map((item) => item?.mapping_id).filter(Boolean), "mapping_id", errors);
144
177
  for (const [index, mapping] of mappings.entries()) {
145
178
  const path = `downstream_mapping[${index}]`;
146
- for (const field of ["domain_change", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
147
- requireArray(mapping ?? {}, "impacts", path, errors, 1);
148
- if (mapping?.propagation && !["direct", "transitive", "stale"].includes(mapping.propagation)) errors.push(`${path}.propagation 非法`);
179
+ if (data.schema_version === 3) {
180
+ for (const field of ["mapping_id", "consumer_capability", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
181
+ for (const field of ["source_refs", "impacts", "evidence_refs"]) requireArray(mapping ?? {}, field, path, errors, 1);
182
+ if (!/^mapping\.[a-z0-9][a-z0-9-]*$/.test(String(mapping?.mapping_id ?? ""))) errors.push(`${path}.mapping_id 格式非法`);
183
+ if (mapping?.consumer_capability && !consumerCapabilities.has(mapping.consumer_capability)) errors.push(`${path}.consumer_capability 非法`);
184
+ for (const reference of mapping?.source_refs ?? []) if (!sourceIds.has(reference)) errors.push(`${path}.source_refs 引用了未声明来源: ${reference}`);
185
+ if (mapping?.propagation && !["direct", "transitive", "not-applicable", "stale"].includes(mapping.propagation)) errors.push(`${path}.propagation 非法`);
186
+ } else {
187
+ for (const field of ["domain_change", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
188
+ requireArray(mapping ?? {}, "impacts", path, errors, 1);
189
+ if (mapping?.propagation && !["direct", "transitive", "stale"].includes(mapping.propagation)) errors.push(`${path}.propagation 非法`);
190
+ }
149
191
  }
150
192
  const approval = data.approval ?? {};
151
193
  for (const field of ["approval_ref", "approver", "persisted_ref", "current_version"]) requireString(approval, field, "approval", errors);
@@ -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 !== 2) errors.push("schema_version 必须为 2");
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", "success_criteria", "test_seams", "confirmed_decisions", "assumptions", "constraints", "evidence_refs"]) requireArray(data, field, "root", errors, ["target_users", "mvp", "success_criteria", "test_seams", "confirmed_decisions", "evidence_refs"].includes(field) ? 1 : 0);
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
- for (const field of ["domain_change", "consumer", "propagation", "reapproval_condition"]) requireString(mapping ?? {}, field, path, errors);
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 ?? {};
@@ -1,16 +1,41 @@
1
- schema_version: 2
1
+ schema_version: 3
2
2
  stage_decision_id: stage-decision.supplier-admission
3
- package_version: v2
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: [新采购订单 100% 校验当前有效准入决定]
10
- test_seams: [supplier-application-submit, admission-validity-check]
11
- confirmed_decisions: [ComplianceReview 拥有准入规则语义,ProcurementExecution 只消费决定]
12
- assumptions: [供应商已有唯一 subject_ref]
13
- constraints: [保留现有采购订单流程]
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: v2
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
- - domain_change: admission-decision-validity
42
- consumer: spec-synthesis
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
- - domain_change: admission-decision-validity
46
- consumer: ticket-decomposition
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: v2
83
+ current_version: v3
@@ -1,7 +1,19 @@
1
- schema_version: 2
1
+ schema_version: 3
2
2
  domain_strategy_id: domain-strategy.supplier-admission
3
- domain_version: v2
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
- - domain_change: admission-decision-validity
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: v2
145
+ current_version: v3