@guanlin4924/devflow-pi 0.1.40 → 0.1.48

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 (50) hide show
  1. package/bin/apply-opencode-go-session-hotfix.mjs +61 -0
  2. package/package.json +2 -1
  3. package/plugins/devflow-core/extensions/devflow-core.ts +609 -42
  4. package/plugins/devflow-core/skills/wireframe-proposal/SKILL.md +13 -0
  5. package/plugins/devflow-core/skills/wireframe-proposal/agents/openai.yaml +4 -0
  6. package/plugins/devflow-core/skills/wireframe-render/SKILL.md +8 -0
  7. package/plugins/devflow-core/skills/wireframe-review/SKILL.md +8 -0
  8. package/plugins/devflow-core/skills/wireframe-validate/SKILL.md +8 -0
  9. package/plugins/devflow-core/skills/wireframe-validate/agents/openai.yaml +4 -0
  10. package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +16 -1
  11. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-candidate-delta.schema.yaml +41 -0
  12. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-claim.schema.yaml +10 -0
  13. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-model-policy.yaml +51 -0
  14. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-model.schema.yaml +241 -0
  15. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/business-source-registry.schema.yaml +80 -0
  16. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/proposal.schema.yaml +44 -0
  17. package/plugins/devflow-core/skills/workflow-orchestrator/contracts/questions.schema.yaml +14 -0
  18. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/acceptance_test_suite.rb +13 -3
  19. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/advance_state.rb +143 -28
  20. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_claim_validate.py +77 -0
  21. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_closure_e2e_seed.json +516 -0
  22. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_cross_model_check.py +76 -0
  23. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_impact.py +51 -0
  24. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_invalidation.rb +108 -0
  25. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_commit.rb +570 -0
  26. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_gate.rb +89 -0
  27. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_render.py +131 -0
  28. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_model_validate.py +1063 -0
  29. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/business_source_resolver.rb +365 -0
  30. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/checker_registry.rb +2 -1
  31. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/execution_contract.rb +3 -3
  32. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_contract.rb +3 -0
  33. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/gate_runtime.rb +13 -4
  34. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_recover.rb +386 -0
  35. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/parallel_run.rb +28 -2
  36. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph.rb +139 -14
  37. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/requirement_graph_backfill.rb +143 -0
  38. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/session_rollover.rb +15 -4
  39. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/sync_artifacts.rb +45 -1
  40. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +10 -1
  41. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_commit.rb +222 -0
  42. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_render.py +231 -0
  43. package/plugins/devflow-core/skills/workflow-orchestrator/scripts/wireframe_validate.py +457 -0
  44. package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +106 -1
  45. package/plugins/devflow-implementation/agents/implementation-worker.md +6 -0
  46. package/plugins/devflow-intel/skills/workflow-code-index/scripts/code_index.py +107 -37
  47. package/plugins/devflow-intel/skills/workflow-code-index/scripts/core/scanner.py +20 -1
  48. package/plugins/devflow-quality/skills/workflow-acceptance-test-design/SKILL.md +1 -1
  49. package/src/constants.ts +1 -1
  50. package/src/runtime.ts +53 -22
@@ -0,0 +1,516 @@
1
+ {
2
+ "renumber": { "REQ-0001": "REQ-0005" },
3
+ "nodes": [
4
+ {
5
+ "id": "BEH-0006",
6
+ "kind": "behavior",
7
+ "status": "candidate",
8
+ "data": {
9
+ "name": "生成前端元素索引并供候选阶段消费",
10
+ "trigger": "代码索引完成后生成前端元素索引",
11
+ "actor_and_authority": "系统生成元素索引;候选阶段只读取已确认版本",
12
+ "preconditions": ["代码索引已完成且有效", "元素索引来源可追溯"],
13
+ "state_transition": "code_indexed -> element_indexed",
14
+ "invariants": ["元素索引必须绑定代码索引版本", "版本不一致不得被消费"],
15
+ "writes": ["element_index_version", "version_relation"],
16
+ "side_effects": ["开放候选生成阶段"],
17
+ "failure_paths": ["元素索引失败", "版本不一致"],
18
+ "recovery_strategy": "阻断候选完成并保留失败原因"
19
+ }
20
+ },
21
+ {
22
+ "id": "BEH-0007",
23
+ "kind": "behavior",
24
+ "status": "candidate",
25
+ "data": {
26
+ "name": "AI 根据双索引生成候选并保留未知 producer",
27
+ "trigger": "代码索引与元素索引就绪后生成候选",
28
+ "actor_and_authority": "AI 生成候选并保留 unknown producer;Core 校验引用",
29
+ "preconditions": ["双索引冻结版本就绪", "候选可引用两个索引版本"],
30
+ "state_transition": "indexed -> candidates_generated",
31
+ "invariants": ["unknown producer 不得猜测填充", "候选必须引用两个索引版本"],
32
+ "writes": ["candidate", "producer_status", "assumptions", "conflicts"],
33
+ "side_effects": ["进入 checker 输入"],
34
+ "failure_paths": ["AI 失败", "AI 超时", "非法输出"],
35
+ "recovery_strategy": "不生成伪成功候选,转交 checker 处理"
36
+ }
37
+ },
38
+ {
39
+ "id": "REQ-0006",
40
+ "kind": "requirement",
41
+ "status": "candidate",
42
+ "data": {
43
+ "fp": "FP-008",
44
+ "input": "已完成且有效的代码索引版本",
45
+ "output": "可消费的前端元素索引版本",
46
+ "behavior": "元素索引来源与代码索引版本可追溯",
47
+ "orchestration": "代码索引完成→元素索引→候选消费",
48
+ "failure": "元素索引失败或版本不一致阻断候选完成",
49
+ "verification": "读取元素索引并核对来源版本",
50
+ "source_refs": ["SRC-0001"],
51
+ "outcome": "可消费的前端元素索引版本",
52
+ "actor_and_authority": "系统根据已完成的代码索引生成元素索引;候选阶段只读取已确认版本",
53
+ "data_scope": "self",
54
+ "behavior_ref": "BEH-0006",
55
+ "failure_refs": ["BC-015"],
56
+ "acceptance_refs": ["AC-0002"],
57
+ "data_scope_detail": "本 feature 内同一 job 生成的代码索引与元素索引版本;不含其他 feature 或租户的索引。"
58
+ }
59
+ },
60
+ {
61
+ "id": "REQ-0007",
62
+ "kind": "requirement",
63
+ "status": "candidate",
64
+ "data": {
65
+ "fp": "FP-009",
66
+ "input": "代码索引 + 前端元素索引冻结版本",
67
+ "output": "AI 候选、producer 状态、assumptions/conflicts",
68
+ "behavior": "AI 根据双索引生成候选,unknown producer 不猜测",
69
+ "orchestration": "双索引就绪→AI 候选→checker 输入",
70
+ "failure": "AI 失败/超时/非法输出不生成伪成功候选",
71
+ "verification": "候选引用两个索引版本且未知事实进入治理字段",
72
+ "source_refs": ["SRC-0001"],
73
+ "outcome": "AI 候选、producer 状态、assumptions/conflicts",
74
+ "actor_and_authority": "AI 生成候选并保留 unknown producer;Core 校验候选引用",
75
+ "data_scope": "self",
76
+ "behavior_ref": "BEH-0007",
77
+ "failure_refs": ["BC-016", "BC-017"],
78
+ "acceptance_refs": ["AC-0003"],
79
+ "data_scope_detail": "候选引用本 feature 的代码索引与元素索引版本;未知 producer 进入治理字段而非被猜测填充。"
80
+ }
81
+ },
82
+ {
83
+ "id": "REQ-0008",
84
+ "kind": "requirement",
85
+ "status": "candidate",
86
+ "data": {
87
+ "fp": "FP-010",
88
+ "input": "含 unknown/conflict 的候选",
89
+ "output": "checker 结果、暂停状态、待处理项",
90
+ "behavior": "checker 暂停并等待用户处理,暂停时下游不可消费",
91
+ "orchestration": "候选→checker→暂停;单一待处理项",
92
+ "failure": "重复检查/会话请求不重复推进",
93
+ "verification": "观察 paused/waiting 状态、原因和待处理项",
94
+ "source_refs": ["SRC-0001"],
95
+ "outcome": "checker 结果、暂停状态、待处理项",
96
+ "actor_and_authority": "checker 记录未知事实并暂停;用户处理待处理项",
97
+ "data_scope": "self",
98
+ "behavior_ref": "BEH-0001",
99
+ "failure_refs": ["BC-018"],
100
+ "acceptance_refs": ["AC-0004"],
101
+ "data_scope_detail": "暂停状态与单一待处理项仅属本 feature job;暂停期间下游不消费。"
102
+ }
103
+ },
104
+ {
105
+ "id": "REQ-0009",
106
+ "kind": "requirement",
107
+ "status": "candidate",
108
+ "data": {
109
+ "fp": "FP-011",
110
+ "input": "用户对待处理事实的处理决定",
111
+ "output": "自动继续后的 canonical 与 approval",
112
+ "behavior": "用户处理后原流程自动继续,Core 生成 canonical/approval",
113
+ "orchestration": "同会话提交处理→checker 重验→Core 顺序产出",
114
+ "failure": "仍冲突或 Core 失败不得声称冻结",
115
+ "verification": "处理前后 revision 关联,canonical/approval 可查询",
116
+ "source_refs": ["SRC-0001"],
117
+ "outcome": "自动继续后的 canonical 与 approval",
118
+ "actor_and_authority": "用户提交处理决定;Core 重验并顺序产出 canonical/approval",
119
+ "data_scope": "self",
120
+ "behavior_ref": "BEH-0002",
121
+ "failure_refs": ["BC-019"],
122
+ "acceptance_refs": ["AC-0005"],
123
+ "data_scope_detail": "处理决定、canonical 与 approval 绑定同一会话 revision;Core 失败不得声称冻结。"
124
+ }
125
+ },
126
+ {
127
+ "id": "REQ-0010",
128
+ "kind": "requirement",
129
+ "status": "candidate",
130
+ "data": {
131
+ "fp": "FP-012",
132
+ "input": "外部回调事件、关联键和状态",
133
+ "output": "成功或失败终态及回调证据",
134
+ "behavior": "外部回调映射成功/失败状态分支",
135
+ "orchestration": "回调接收→校验→状态映射→终态",
136
+ "failure": "超时、重复、乱序、未知关联或非法来源不误推进",
137
+ "verification": "两条分支均可重放且有事件/终态证据",
138
+ "source_refs": ["SRC-0001"],
139
+ "outcome": "成功或失败终态及回调证据",
140
+ "actor_and_authority": "外部系统发起回调;系统校验关联并映射终态",
141
+ "data_scope": "self",
142
+ "behavior_ref": "BEH-0003",
143
+ "failure_refs": ["BC-020", "BC-021"],
144
+ "acceptance_refs": ["AC-0006"],
145
+ "data_scope_detail": "回调关联本 feature job;成功/失败两条分支均有可重放证据。"
146
+ }
147
+ },
148
+ {
149
+ "id": "REQ-0011",
150
+ "kind": "requirement",
151
+ "status": "candidate",
152
+ "data": {
153
+ "fp": "FP-013",
154
+ "input": "Core canonical/approval 冻结版本与同一会话上下文",
155
+ "output": "下游消费记录、fingerprint/revision、会话连续性证据",
156
+ "behavior": "下游只消费冻结版本且无需关闭/重启会话",
157
+ "orchestration": "冻结→下游读取→核对 fingerprint;原会话继续",
158
+ "failure": "草稿/旧版本/未冻结版本拒绝;中断不得伪报完成",
159
+ "verification": "消费请求引用冻结 revision/fingerprint;会话 ID 前后一致",
160
+ "source_refs": ["SRC-0001"],
161
+ "outcome": "下游消费记录、fingerprint/revision、会话连续性证据",
162
+ "actor_and_authority": "下游只读取冻结版本并核对 fingerprint;Core 提供冻结 revision",
163
+ "data_scope": "self",
164
+ "behavior_ref": "BEH-0004",
165
+ "failure_refs": ["BC-022"],
166
+ "acceptance_refs": ["AC-0007"],
167
+ "data_scope_detail": "下游消费记录引用冻结 revision/fingerprint;会话 ID 前后一致,无需重启。"
168
+ }
169
+ },
170
+ {
171
+ "id": "AC-0002",
172
+ "kind": "acceptance",
173
+ "status": "candidate",
174
+ "data": {
175
+ "requirement_refs": ["REQ-0006"],
176
+ "behavior_ref": "BEH-0006",
177
+ "outcome": "代码索引完成后必须生成可追溯的前端元素索引版本",
178
+ "verification": "读取元素索引并核对来源代码索引版本",
179
+ "notes": "对应 FP-008 / REQ-0006 的元素索引验收",
180
+ "requirement_ref": "REQ-0006",
181
+ "verification_types": ["business", "contract", "resilience"],
182
+ "title": "生成前端元素索引并供候选阶段消费",
183
+ "source_refs": ["SRC-0001"],
184
+ "business_verification": "代码索引完成后必须生成可消费的元素索引;元素索引与代码索引版本可追溯。",
185
+ "contract_verification": "元素索引版本与来源代码索引版本可读取并可核对一致;不一致时不得被候选阶段消费。",
186
+ "resilience_verification": "元素索引失败或版本不一致时阻断候选完成,保留失败原因。"
187
+ }
188
+ },
189
+ {
190
+ "id": "AC-0003",
191
+ "kind": "acceptance",
192
+ "status": "candidate",
193
+ "data": {
194
+ "requirement_refs": ["REQ-0007"],
195
+ "behavior_ref": "BEH-0007",
196
+ "outcome": "AI 根据双索引生成候选,unknown producer 不猜测填充",
197
+ "verification": "候选引用两个索引版本且未知事实进入治理字段",
198
+ "notes": "对应 FP-009 / REQ-0007 的候选生成验收",
199
+ "requirement_ref": "REQ-0007",
200
+ "verification_types": ["business", "contract", "resilience"],
201
+ "title": "AI 根据双索引生成候选并保留未知 producer",
202
+ "source_refs": ["SRC-0001"],
203
+ "business_verification": "双索引就绪后 AI 生成候选;unknown producer 显式保留为 unknown,不猜测填充。",
204
+ "contract_verification": "候选引用两个索引版本;未知事实进入 assumptions/conflicts 治理字段。",
205
+ "resilience_verification": "AI 失败、超时或非法输出不生成伪成功候选。"
206
+ }
207
+ },
208
+ {
209
+ "id": "AC-0004",
210
+ "kind": "acceptance",
211
+ "status": "candidate",
212
+ "data": {
213
+ "requirement_refs": ["REQ-0008"],
214
+ "behavior_ref": "BEH-0001",
215
+ "outcome": "checker 记录未知事实并暂停等待用户处理",
216
+ "verification": "观察 paused/waiting 状态、原因和待处理项",
217
+ "notes": "对应 FP-010 / REQ-0008 的暂停验收",
218
+ "requirement_ref": "REQ-0008",
219
+ "verification_types": ["business", "contract", "resilience"],
220
+ "title": "checker 记录未知事实并暂停等待用户处理",
221
+ "source_refs": ["SRC-0001"],
222
+ "business_verification": "含 unknown/conflict 的候选被 checker 拦截并暂停;暂停时下游不可消费。",
223
+ "contract_verification": "paused/waiting 状态、原因和单一待处理项可观察。",
224
+ "resilience_verification": "重复检查或重复会话请求不重复推进状态。"
225
+ }
226
+ },
227
+ {
228
+ "id": "AC-0005",
229
+ "kind": "acceptance",
230
+ "status": "candidate",
231
+ "data": {
232
+ "requirement_refs": ["REQ-0009"],
233
+ "behavior_ref": "BEH-0002",
234
+ "outcome": "用户处理后原流程自动继续并生成 Core canonical/approval",
235
+ "verification": "处理前后 revision 关联,canonical/approval 可查询",
236
+ "notes": "对应 FP-011 / REQ-0009 的自动继续验收",
237
+ "requirement_ref": "REQ-0009",
238
+ "verification_types": ["business", "contract", "resilience"],
239
+ "title": "用户处理后同一会话自动继续并生成 Core canonical/approval",
240
+ "source_refs": ["SRC-0001"],
241
+ "business_verification": "用户提交处理决定后原流程自动继续,Core 生成 canonical 与 approval。",
242
+ "contract_verification": "处理前后 revision 关联;canonical/approval 可查询。",
243
+ "resilience_verification": "仍存在冲突或 Core 失败时不得声称冻结。"
244
+ }
245
+ },
246
+ {
247
+ "id": "AC-0006",
248
+ "kind": "acceptance",
249
+ "status": "candidate",
250
+ "data": {
251
+ "requirement_refs": ["REQ-0010"],
252
+ "behavior_ref": "BEH-0003",
253
+ "outcome": "外部回调驱动成功与失败状态分支并保留证据",
254
+ "verification": "两条分支均可重放且有事件/终态证据",
255
+ "notes": "对应 FP-012 / REQ-0010 的回调分支验收",
256
+ "requirement_ref": "REQ-0010",
257
+ "verification_types": ["business", "contract", "resilience"],
258
+ "title": "外部系统回调驱动成功与失败状态分支",
259
+ "source_refs": ["SRC-0001"],
260
+ "business_verification": "外部回调映射成功/失败状态分支并进入终态。",
261
+ "contract_verification": "成功与失败两条分支均可重放且具有事件/终态证据。",
262
+ "resilience_verification": "超时、重复、乱序、未知关联或非法来源不误推进状态。"
263
+ }
264
+ },
265
+ {
266
+ "id": "AC-0007",
267
+ "kind": "acceptance",
268
+ "status": "candidate",
269
+ "data": {
270
+ "requirement_refs": ["REQ-0011"],
271
+ "behavior_ref": "BEH-0004",
272
+ "outcome": "下游只消费冻结版本且会话无需重启",
273
+ "verification": "消费请求引用冻结 revision/fingerprint;会话 ID 前后一致",
274
+ "notes": "对应 FP-013 / REQ-0011 的冻结消费验收",
275
+ "requirement_ref": "REQ-0011",
276
+ "verification_types": ["business", "contract", "resilience"],
277
+ "title": "下游只消费冻结版本且会话无需重启",
278
+ "source_refs": ["SRC-0001"],
279
+ "business_verification": "下游只读取冻结 canonical/approval 版本,无需关闭或重启会话。",
280
+ "contract_verification": "消费请求引用冻结 revision/fingerprint;会话 ID 前后一致。",
281
+ "resilience_verification": "草稿、旧版本或未冻结版本被拒绝;中断不得伪报完成。"
282
+ }
283
+ },
284
+ {
285
+ "id": "TC-0004",
286
+ "kind": "test_case",
287
+ "status": "candidate",
288
+ "data": {
289
+ "acceptance_refs": ["AC-0002"],
290
+ "requirement_refs": ["REQ-0006"],
291
+ "test_type": "api",
292
+ "target": "代码索引完成后生成可追溯前端元素索引版本",
293
+ "preconditions": ["代码索引已完成且有效", "元素索引生成能力可用"],
294
+ "steps": ["触发元素索引生成", "读取元素索引版本与来源代码索引版本", "核对版本关系"],
295
+ "oracle": "元素索引生成成功且与来源代码索引版本可追溯;候选阶段可读取已确认版本。",
296
+ "automation_mode": "automated",
297
+ "evidence_strategy": "replay",
298
+ "replay_scope": "integration"
299
+ }
300
+ },
301
+ {
302
+ "id": "TC-0005",
303
+ "kind": "test_case",
304
+ "status": "candidate",
305
+ "data": {
306
+ "acceptance_refs": ["AC-0002"],
307
+ "requirement_refs": ["REQ-0006"],
308
+ "test_type": "boundary",
309
+ "target": "元素索引失败或版本不一致阻断候选完成",
310
+ "preconditions": ["代码索引已完成", "可构造索引失败或版本不一致场景"],
311
+ "steps": ["触发元素索引失败", "观察阻断结果与失败原因", "尝试用不一致版本进入候选"],
312
+ "oracle": "元素索引失败或版本不一致时阻断候选完成,失败原因保留;不一致版本不被消费。",
313
+ "automation_mode": "automated",
314
+ "evidence_strategy": "replay",
315
+ "replay_scope": "integration"
316
+ }
317
+ },
318
+ {
319
+ "id": "TC-0006",
320
+ "kind": "test_case",
321
+ "status": "candidate",
322
+ "data": {
323
+ "acceptance_refs": ["AC-0003"],
324
+ "requirement_refs": ["REQ-0007"],
325
+ "test_type": "api",
326
+ "target": "AI 根据双索引冻结版本生成候选并保留 unknown producer",
327
+ "preconditions": ["双索引冻结版本就绪", "AI 候选能力可用"],
328
+ "steps": ["提交双索引版本", "触发候选生成", "读取候选与 producer 状态"],
329
+ "oracle": "候选引用两个索引版本;unknown producer 显式保留为 unknown,不猜测填充。",
330
+ "automation_mode": "automated",
331
+ "evidence_strategy": "replay",
332
+ "replay_scope": "integration"
333
+ }
334
+ },
335
+ {
336
+ "id": "TC-0007",
337
+ "kind": "test_case",
338
+ "status": "candidate",
339
+ "data": {
340
+ "acceptance_refs": ["AC-0003"],
341
+ "requirement_refs": ["REQ-0007"],
342
+ "test_type": "boundary",
343
+ "target": "AI 失败、超时或非法输出不生成伪成功候选",
344
+ "preconditions": ["双索引就绪", "可注入 AI 失败/超时/非法输出"],
345
+ "steps": ["注入 AI 失败", "注入超时", "注入非法输出", "观察候选结果"],
346
+ "oracle": "AI 失败、超时或非法输出均不生成伪成功候选;未知事实进入治理字段。",
347
+ "automation_mode": "automated",
348
+ "evidence_strategy": "replay",
349
+ "replay_scope": "integration"
350
+ }
351
+ },
352
+ {
353
+ "id": "TC-0008",
354
+ "kind": "test_case",
355
+ "status": "candidate",
356
+ "data": {
357
+ "acceptance_refs": ["AC-0004"],
358
+ "requirement_refs": ["REQ-0008"],
359
+ "test_type": "api",
360
+ "target": "checker 记录 unknown/conflict 并暂停等待用户处理",
361
+ "preconditions": ["存在含 unknown/conflict 的候选", "checker 可运行"],
362
+ "steps": ["提交含 unknown 的候选", "运行 checker", "读取暂停状态与待处理项"],
363
+ "oracle": "checker 记录未知事实并暂停;paused/waiting 状态、原因与单一待处理项可观察。",
364
+ "automation_mode": "automated",
365
+ "evidence_strategy": "replay",
366
+ "replay_scope": "integration"
367
+ }
368
+ },
369
+ {
370
+ "id": "TC-0009",
371
+ "kind": "test_case",
372
+ "status": "candidate",
373
+ "data": {
374
+ "acceptance_refs": ["AC-0004"],
375
+ "requirement_refs": ["REQ-0008"],
376
+ "test_type": "boundary",
377
+ "target": "重复检查或重复会话请求不重复推进状态",
378
+ "preconditions": ["流程已处于暂停状态", "可重复触发检查或会话请求"],
379
+ "steps": ["重复运行 checker", "重复发起会话请求", "观察状态是否重复推进"],
380
+ "oracle": "重复检查或重复会话请求不重复推进;单一待处理项保持不变。",
381
+ "automation_mode": "automated",
382
+ "evidence_strategy": "replay",
383
+ "replay_scope": "integration"
384
+ }
385
+ },
386
+ {
387
+ "id": "TC-0010",
388
+ "kind": "test_case",
389
+ "status": "candidate",
390
+ "data": {
391
+ "acceptance_refs": ["AC-0005"],
392
+ "requirement_refs": ["REQ-0009"],
393
+ "test_type": "api",
394
+ "target": "用户处理后原流程自动继续并生成 Core canonical/approval",
395
+ "preconditions": ["流程已暂停且有待处理项", "用户可提交处理决定"],
396
+ "steps": ["提交处理决定", "触发 checker 重验", "读取 canonical/approval"],
397
+ "oracle": "用户处理后原流程自动继续,Core 生成 canonical/approval;处理前后 revision 关联。",
398
+ "automation_mode": "automated",
399
+ "evidence_strategy": "replay",
400
+ "replay_scope": "integration"
401
+ }
402
+ },
403
+ {
404
+ "id": "TC-0011",
405
+ "kind": "test_case",
406
+ "status": "candidate",
407
+ "data": {
408
+ "acceptance_refs": ["AC-0005"],
409
+ "requirement_refs": ["REQ-0009"],
410
+ "test_type": "boundary",
411
+ "target": "仍存在冲突或 Core 失败时不得声称冻结",
412
+ "preconditions": ["存在待处理冲突", "可注入 Core 失败"],
413
+ "steps": ["提交不足以解决冲突的决定", "注入 Core 失败", "观察是否声称冻结"],
414
+ "oracle": "仍冲突或 Core 失败时不得声称冻结;canonical/approval 不可查询为冻结。",
415
+ "automation_mode": "automated",
416
+ "evidence_strategy": "replay",
417
+ "replay_scope": "integration"
418
+ }
419
+ },
420
+ {
421
+ "id": "TC-0012",
422
+ "kind": "test_case",
423
+ "status": "candidate",
424
+ "data": {
425
+ "acceptance_refs": ["AC-0006"],
426
+ "requirement_refs": ["REQ-0010"],
427
+ "test_type": "api",
428
+ "target": "外部回调成功映射到成功终态",
429
+ "preconditions": ["流程已注册等待回调", "回调关联键与签名已绑定"],
430
+ "steps": ["触发一次成功回调", "校验关联键", "读取终态与回调证据"],
431
+ "oracle": "成功回调映射到成功终态;事件与终态证据可查询。",
432
+ "automation_mode": "automated",
433
+ "evidence_strategy": "replay",
434
+ "replay_scope": "integration"
435
+ }
436
+ },
437
+ {
438
+ "id": "TC-0013",
439
+ "kind": "test_case",
440
+ "status": "candidate",
441
+ "data": {
442
+ "acceptance_refs": ["AC-0006"],
443
+ "requirement_refs": ["REQ-0010"],
444
+ "risk_refs": ["RISK-0001"],
445
+ "test_type": "resilience",
446
+ "target": "外部回调失败与异常输入不误推进",
447
+ "preconditions": ["回调供应商、签名方式、输入格式作为夹具显式绑定", "流程已注册等待回调"],
448
+ "steps": ["触发一次失败回调", "触发超时/重复/乱序/未知关联", "重复同一绑定配置重放"],
449
+ "oracle": "失败回调映射到失败终态;超时、重复、乱序、未知关联或非法来源不误推进;成功与失败分支均可重放。",
450
+ "automation_mode": "automated",
451
+ "evidence_strategy": "replay",
452
+ "replay_scope": "integration"
453
+ }
454
+ },
455
+ {
456
+ "id": "TC-0014",
457
+ "kind": "test_case",
458
+ "status": "candidate",
459
+ "data": {
460
+ "acceptance_refs": ["AC-0007"],
461
+ "requirement_refs": ["REQ-0011"],
462
+ "test_type": "api",
463
+ "target": "下游只消费冻结版本且会话无需重启",
464
+ "preconditions": ["canonical/approval 已冻结", "原会话仍存活"],
465
+ "steps": ["下游读取冻结版本", "核对 revision/fingerprint", "确认会话 ID 前后一致"],
466
+ "oracle": "下游读取冻结 revision/fingerprint;原会话无需关闭或重启即继续。",
467
+ "automation_mode": "automated",
468
+ "evidence_strategy": "replay",
469
+ "replay_scope": "integration"
470
+ }
471
+ },
472
+ {
473
+ "id": "TC-0015",
474
+ "kind": "test_case",
475
+ "status": "candidate",
476
+ "data": {
477
+ "acceptance_refs": ["AC-0007"],
478
+ "requirement_refs": ["REQ-0011"],
479
+ "test_type": "boundary",
480
+ "target": "草稿/旧版本/未冻结版本被拒绝",
481
+ "preconditions": ["存在草稿或未冻结版本", "下游尝试消费"],
482
+ "steps": ["尝试消费草稿版本", "尝试消费旧版本", "尝试消费未冻结版本"],
483
+ "oracle": "草稿、旧版本或未冻结版本被拒绝;中断不得伪报完成。",
484
+ "automation_mode": "automated",
485
+ "evidence_strategy": "replay",
486
+ "replay_scope": "integration"
487
+ }
488
+ }
489
+ ],
490
+ "edges": [
491
+ { "from": "AC-0002", "to": "TC-0004", "relation": "verified_by" },
492
+ { "from": "REQ-0006", "to": "TC-0004", "relation": "verified_by" },
493
+ { "from": "AC-0002", "to": "TC-0005", "relation": "verified_by" },
494
+ { "from": "REQ-0006", "to": "TC-0005", "relation": "verified_by" },
495
+ { "from": "AC-0003", "to": "TC-0006", "relation": "verified_by" },
496
+ { "from": "REQ-0007", "to": "TC-0006", "relation": "verified_by" },
497
+ { "from": "AC-0003", "to": "TC-0007", "relation": "verified_by" },
498
+ { "from": "REQ-0007", "to": "TC-0007", "relation": "verified_by" },
499
+ { "from": "AC-0004", "to": "TC-0008", "relation": "verified_by" },
500
+ { "from": "REQ-0008", "to": "TC-0008", "relation": "verified_by" },
501
+ { "from": "AC-0004", "to": "TC-0009", "relation": "verified_by" },
502
+ { "from": "REQ-0008", "to": "TC-0009", "relation": "verified_by" },
503
+ { "from": "AC-0005", "to": "TC-0010", "relation": "verified_by" },
504
+ { "from": "REQ-0009", "to": "TC-0010", "relation": "verified_by" },
505
+ { "from": "AC-0005", "to": "TC-0011", "relation": "verified_by" },
506
+ { "from": "REQ-0009", "to": "TC-0011", "relation": "verified_by" },
507
+ { "from": "AC-0006", "to": "TC-0012", "relation": "verified_by" },
508
+ { "from": "REQ-0010", "to": "TC-0012", "relation": "verified_by" },
509
+ { "from": "AC-0006", "to": "TC-0013", "relation": "verified_by" },
510
+ { "from": "REQ-0010", "to": "TC-0013", "relation": "verified_by" },
511
+ { "from": "AC-0007", "to": "TC-0014", "relation": "verified_by" },
512
+ { "from": "REQ-0011", "to": "TC-0014", "relation": "verified_by" },
513
+ { "from": "AC-0007", "to": "TC-0015", "relation": "verified_by" },
514
+ { "from": "REQ-0011", "to": "TC-0015", "relation": "verified_by" }
515
+ ]
516
+ }
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ """Deterministically close business references across wireframe and claims."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+
12
+ def read_json(path: Path) -> Any:
13
+ return json.loads(path.read_text(encoding="utf-8"))
14
+
15
+
16
+ def read_yaml(path: Path) -> Any:
17
+ try:
18
+ import yaml # type: ignore
19
+ return yaml.safe_load(path.read_text(encoding="utf-8"))
20
+ except ImportError:
21
+ result = subprocess.run(["ruby", "-ryaml", "-rjson", "-e", "puts JSON.generate(YAML.safe_load(File.read(ARGV.fetch(0))))", str(path)], capture_output=True, text=True, check=False)
22
+ if result.returncode:
23
+ raise ValueError(result.stderr.strip() or "YAML parse failed")
24
+ return json.loads(result.stdout)
25
+
26
+
27
+ def main() -> int:
28
+ parser = argparse.ArgumentParser()
29
+ parser.add_argument("--business-canonical", type=Path, required=True)
30
+ parser.add_argument("--wireframe", type=Path)
31
+ parser.add_argument("--claims", type=Path)
32
+ parser.add_argument("--traceability", type=Path)
33
+ parser.add_argument("--changed-business-ref", action="append", default=[])
34
+ parser.add_argument("--json", action="store_true")
35
+ args = parser.parse_args()
36
+ errors = []
37
+ consumers = []
38
+ try:
39
+ business = read_json(args.business_canonical)
40
+ actions = {item.get("id") for item in business.get("actions", []) if isinstance(item, dict)}
41
+ requirements = set()
42
+ if args.traceability:
43
+ traceability = read_json(args.traceability)
44
+ requirements = {item.get("id") for item in traceability.get("requirements", []) if isinstance(item, dict) and item.get("status") in {"confirmed", "approved"}}
45
+ if args.wireframe:
46
+ wireframe = read_json(args.wireframe)
47
+ for screen in wireframe.get("screens", []):
48
+ for action in screen.get("actions", []):
49
+ ref = action.get("business_action_ref")
50
+ if action.get("type") == "open" and (not ref or ref not in actions):
51
+ errors.append({"code": "BUSINESS_WIREFRAME_REF_UNRESOLVED", "screen": screen.get("id"), "action": action.get("id"), "ref": ref})
52
+ if ref in set(args.changed_business_ref):
53
+ consumers.append({"kind": "wireframe", "id": screen.get("id"), "ref": ref})
54
+ if args.claims:
55
+ claims = read_yaml(args.claims)
56
+ for claim in (claims.get("claims") if isinstance(claims, dict) else claims) or []:
57
+ if not isinstance(claim, dict):
58
+ continue
59
+ ref = claim.get("business_action_ref")
60
+ req = claim.get("requirement_ref")
61
+ if ref and ref not in actions:
62
+ errors.append({"code": "BUSINESS_CLAIM_REF_UNRESOLVED", "id": claim.get("id"), "ref": ref})
63
+ if req and req not in requirements:
64
+ errors.append({"code": "BUSINESS_CLAIM_REQUIREMENT_UNRESOLVED", "id": claim.get("id"), "ref": req})
65
+ if ref in set(args.changed_business_ref):
66
+ consumers.append({"kind": "claim", "id": claim.get("id"), "ref": ref})
67
+ payload = {"valid": not errors, "verdict": "block_unsafe" if errors else "allow", "errors": errors, "affected_consumers": consumers}
68
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
69
+ return 1 if errors else 0
70
+ except Exception as error:
71
+ print(json.dumps({"valid": False, "verdict": "block_unsafe", "errors": [{"code": "BUSINESS_CROSS_MODEL_INVALID", "message": str(error)}]}, ensure_ascii=False))
72
+ return 1
73
+
74
+
75
+ if __name__ == "__main__":
76
+ raise SystemExit(main())
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env python3
2
+ """Compute precise business-model invalidation from changed source IDs."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ COLLECTIONS = ["actors", "entities", "components", "boundaries", "resources", "rules", "actions"]
12
+
13
+
14
+ def collect_refs(value: Any, source_ids: set[str]) -> set[str]:
15
+ if isinstance(value, dict):
16
+ direct = set(value.get("source_refs", [])) & source_ids
17
+ nested = set()
18
+ for item in value.values():
19
+ nested |= collect_refs(item, source_ids)
20
+ return direct | nested
21
+ if isinstance(value, list):
22
+ result = set()
23
+ for item in value:
24
+ result |= collect_refs(item, source_ids)
25
+ return result
26
+ return set()
27
+
28
+
29
+ def main() -> int:
30
+ parser = argparse.ArgumentParser()
31
+ parser.add_argument("--canonical", type=Path, required=True)
32
+ parser.add_argument("--changed-source", action="append", required=True)
33
+ args = parser.parse_args()
34
+ try:
35
+ model = json.loads(args.canonical.read_text(encoding="utf-8"))
36
+ except Exception as error:
37
+ print(json.dumps({"ok": False, "verdict": "block_unsafe", "errors": [{"code": "BUSINESS_CANONICAL_INVALID", "message": str(error)}]}, ensure_ascii=False))
38
+ return 1
39
+ changed = set(args.changed_source)
40
+ affected = []
41
+ for collection in COLLECTIONS:
42
+ for item in model.get(collection, []):
43
+ if isinstance(item, dict) and collect_refs(item, changed):
44
+ affected.append({"collection": collection, "id": item.get("id"), "changed_sources": sorted(collect_refs(item, changed))})
45
+ payload = {"ok": True, "verdict": "allow", "changed_sources": sorted(changed), "affected": affected, "affected_ids": [item["id"] for item in affected]}
46
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
47
+ return 0
48
+
49
+
50
+ if __name__ == "__main__":
51
+ raise SystemExit(main())