@guanlin4924/devflow-pi 0.1.14 → 0.1.15
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/package.json +1 -1
- package/plugins/devflow-core/skills/workflow-orchestrator/SKILL.md +4 -4
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/traceability_check.py +160 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/scripts/validate_state.rb +27 -0
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml +25 -3
- package/plugins/devflow-core/skills/workflow-orchestrator/templates/traceability.json +23 -0
- package/plugins/devflow-design/skills/workflow-api-contract/SKILL.md +4 -1
- package/plugins/devflow-design/skills/workflow-backend-tech-spec/SKILL.md +4 -0
- package/plugins/devflow-design/skills/workflow-frontend-tech-spec/SKILL.md +4 -0
- package/plugins/devflow-design/skills/workflow-integration-verify/SKILL.md +10 -0
- package/plugins/devflow-implementation/skills/workflow-change-plan/SKILL.md +8 -0
- package/plugins/devflow-requirements/skills/workflow-requirement-baseline/SKILL.md +9 -0
package/package.json
CHANGED
|
@@ -115,14 +115,14 @@ After each step completes, verify the step's gate before advancing:
|
|
|
115
115
|
| requirement-wayfinder | 决策地图.meta.json `status=ready_for_baseline` AND `ready_for_baseline=true` | Reject advance to domain-modeling |
|
|
116
116
|
| domain-modeling | 领域模型记录及 meta 存在且标识当前功能 | Reject advance to requirement-feasibility-review |
|
|
117
117
|
| requirement-feasibility-review | 可实现性审查.meta.json `status=ready_for_baseline`、`baseline_gate=pass`、`gap_count=0` 且无 critical finding | Reject advance to requirement-baseline |
|
|
118
|
-
| requirement-baseline | baseline.md review conclusion is "通过" AND baseline.meta.json `review_status`
|
|
119
|
-
| api-contract | api-contract.meta.json `review_status`
|
|
118
|
+
| requirement-baseline | baseline.md review conclusion is "通过" AND baseline.meta.json `review_status` / `traceability_status` are `passed` AND blocking counters are all 0 | Reject advance to api-contract |
|
|
119
|
+
| api-contract | api-contract.meta.json `review_status` / `traceability_status` are `passed` AND `pending_contract_count` is 0 | Reject advance to dual tracks |
|
|
120
120
|
| ui-spec | UI-SPEC.md review conclusion is "通过" | Reject advance to feature-breakdown |
|
|
121
121
|
| feature-breakdown | 功能点梳理.md review conclusion is "通过" | Reject advance to tech-specs |
|
|
122
122
|
| api-call-guide | api-call-guide.meta.json `frontend_consumable` and `mock_ready` are true, pending_items is empty | Reject merge gate |
|
|
123
123
|
| frontend-tech-spec + backend-tech-spec | Both review conclusions are "通过" | Reject integration-verify |
|
|
124
|
-
| integration-verify | integration-report.meta.json `merge_gate_status`
|
|
125
|
-
| change-plan | 变更清单.md review conclusion is "通过" | Reject advance to execute |
|
|
124
|
+
| integration-verify | integration-report.meta.json `merge_gate_status` / `traceability_status` are `passed` and all mismatch/blocking counters are 0 | Reject advance to change-plan |
|
|
125
|
+
| change-plan | 变更清单.md review conclusion is "通过" AND 变更清单.meta.json traceability_status is `passed` with zero uncovered/orphan counts | Reject advance to execute |
|
|
126
126
|
|
|
127
127
|
If a gate fails, the current step must be re-executed. Do not skip failing gates.
|
|
128
128
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Fail-closed requirement-to-delivery traceability validation."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
STAGES = ("baseline", "design", "change-plan", "verify")
|
|
11
|
+
DIMENSIONS = ("input", "output", "behavior", "orchestration", "failure", "verification")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def fail(errors: list[str], message: str) -> None:
|
|
15
|
+
errors.append(message)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def references(items: object) -> set[str]:
|
|
19
|
+
if not isinstance(items, list):
|
|
20
|
+
return set()
|
|
21
|
+
result = set()
|
|
22
|
+
for item in items:
|
|
23
|
+
if isinstance(item, str):
|
|
24
|
+
result.add(item)
|
|
25
|
+
elif isinstance(item, dict) and isinstance(item.get("id"), str):
|
|
26
|
+
result.add(item["id"])
|
|
27
|
+
return result
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def check(data: dict, stage: str) -> list[str]:
|
|
31
|
+
errors: list[str] = []
|
|
32
|
+
requirements = data.get("requirements")
|
|
33
|
+
if data.get("schema_version") != 1:
|
|
34
|
+
fail(errors, "schema_version must be 1")
|
|
35
|
+
if not isinstance(data.get("feature"), str) or not data["feature"].strip():
|
|
36
|
+
fail(errors, "feature is required")
|
|
37
|
+
if not isinstance(requirements, list) or not requirements:
|
|
38
|
+
return errors + ["requirements must be a non-empty array"]
|
|
39
|
+
|
|
40
|
+
active: set[str] = set()
|
|
41
|
+
known: set[str] = set()
|
|
42
|
+
for req in requirements:
|
|
43
|
+
if not isinstance(req, dict):
|
|
44
|
+
fail(errors, "every requirement must be an object")
|
|
45
|
+
continue
|
|
46
|
+
rid = req.get("id")
|
|
47
|
+
if not isinstance(rid, str) or not rid.startswith("REQ-"):
|
|
48
|
+
fail(errors, f"invalid requirement id: {rid!r}")
|
|
49
|
+
continue
|
|
50
|
+
known.add(rid)
|
|
51
|
+
if req.get("status", "confirmed") in {"confirmed", "approved"} and req.get("scope", "in") == "in":
|
|
52
|
+
active.add(rid)
|
|
53
|
+
dimensions = req.get("dimensions")
|
|
54
|
+
if not isinstance(dimensions, dict):
|
|
55
|
+
fail(errors, f"{rid}: dimensions is required")
|
|
56
|
+
else:
|
|
57
|
+
missing = [d for d in DIMENSIONS if not isinstance(dimensions.get(d), str) or not dimensions[d].strip()]
|
|
58
|
+
if missing:
|
|
59
|
+
fail(errors, f"{rid}: missing dimensions: {', '.join(missing)}")
|
|
60
|
+
|
|
61
|
+
if stage == "baseline":
|
|
62
|
+
return errors
|
|
63
|
+
|
|
64
|
+
designs = data.get("designs")
|
|
65
|
+
if not isinstance(designs, list) or not designs:
|
|
66
|
+
fail(errors, "designs must be a non-empty array after baseline")
|
|
67
|
+
covered_by_design = set()
|
|
68
|
+
for item in designs or []:
|
|
69
|
+
if not isinstance(item, dict) or not isinstance(item.get("id"), str):
|
|
70
|
+
fail(errors, "every design needs an id")
|
|
71
|
+
continue
|
|
72
|
+
refs = references(item.get("requirements"))
|
|
73
|
+
unknown = refs - known
|
|
74
|
+
if unknown:
|
|
75
|
+
fail(errors, f"{item['id']}: references unknown requirements: {', '.join(sorted(unknown))}")
|
|
76
|
+
covered_by_design |= refs
|
|
77
|
+
if item.get("kind") == "api":
|
|
78
|
+
contract = item.get("contract")
|
|
79
|
+
if not isinstance(contract, dict) or any(not contract.get(k) for k in ("request", "response", "errors", "behavior")):
|
|
80
|
+
fail(errors, f"{item['id']}: API design must include request, response, errors and behavior")
|
|
81
|
+
missing = active - covered_by_design
|
|
82
|
+
if missing:
|
|
83
|
+
fail(errors, "requirements missing design coverage: " + ", ".join(sorted(missing)))
|
|
84
|
+
if stage == "design":
|
|
85
|
+
return errors
|
|
86
|
+
|
|
87
|
+
tasks = data.get("tasks")
|
|
88
|
+
if not isinstance(tasks, list) or not tasks:
|
|
89
|
+
fail(errors, "tasks must be a non-empty array after design")
|
|
90
|
+
covered_by_task = set()
|
|
91
|
+
for task in tasks or []:
|
|
92
|
+
if not isinstance(task, dict) or not isinstance(task.get("id"), str):
|
|
93
|
+
fail(errors, "every task needs an id")
|
|
94
|
+
continue
|
|
95
|
+
refs = task.get("requirements")
|
|
96
|
+
if not isinstance(refs, list) or not refs:
|
|
97
|
+
fail(errors, f"{task['id']}: requirements is required")
|
|
98
|
+
else:
|
|
99
|
+
covered_by_task.update(refs)
|
|
100
|
+
unknown = set(refs) - known
|
|
101
|
+
if unknown:
|
|
102
|
+
fail(errors, f"{task['id']}: references unknown requirements: {', '.join(sorted(unknown))}")
|
|
103
|
+
criteria = task.get("acceptance")
|
|
104
|
+
if not isinstance(criteria, dict):
|
|
105
|
+
fail(errors, f"{task['id']}: acceptance is required")
|
|
106
|
+
else:
|
|
107
|
+
missing = [d for d in DIMENSIONS if not isinstance(criteria.get(d), str) or not criteria[d].strip()]
|
|
108
|
+
if missing:
|
|
109
|
+
fail(errors, f"{task['id']}: missing acceptance dimensions: {', '.join(missing)}")
|
|
110
|
+
missing = active - covered_by_task
|
|
111
|
+
if missing:
|
|
112
|
+
fail(errors, "requirements missing task coverage: " + ", ".join(sorted(missing)))
|
|
113
|
+
if stage == "change-plan":
|
|
114
|
+
return errors
|
|
115
|
+
|
|
116
|
+
verification = data.get("verification")
|
|
117
|
+
if not isinstance(verification, list) or not verification:
|
|
118
|
+
fail(errors, "verification must be a non-empty array after change-plan")
|
|
119
|
+
covered_by_verification = set()
|
|
120
|
+
for item in verification or []:
|
|
121
|
+
if not isinstance(item, dict) or not isinstance(item.get("id"), str):
|
|
122
|
+
fail(errors, "every verification item needs an id")
|
|
123
|
+
continue
|
|
124
|
+
refs = item.get("requirements")
|
|
125
|
+
if not isinstance(refs, list) or not refs:
|
|
126
|
+
fail(errors, f"{item['id']}: requirements is required")
|
|
127
|
+
else:
|
|
128
|
+
covered_by_verification.update(refs)
|
|
129
|
+
unknown = set(refs) - known
|
|
130
|
+
if unknown:
|
|
131
|
+
fail(errors, f"{item['id']}: references unknown requirements: {', '.join(sorted(unknown))}")
|
|
132
|
+
if not isinstance(item.get("evidence"), str) or not item["evidence"].strip():
|
|
133
|
+
fail(errors, f"{item['id']}: evidence is required")
|
|
134
|
+
missing = active - covered_by_verification
|
|
135
|
+
if missing:
|
|
136
|
+
fail(errors, "requirements missing verification coverage: " + ", ".join(sorted(missing)))
|
|
137
|
+
return errors
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def main() -> int:
|
|
141
|
+
parser = argparse.ArgumentParser()
|
|
142
|
+
parser.add_argument("--file", required=True)
|
|
143
|
+
parser.add_argument("--stage", choices=STAGES, required=True)
|
|
144
|
+
args = parser.parse_args()
|
|
145
|
+
try:
|
|
146
|
+
data = json.loads(Path(args.file).read_text(encoding="utf-8"))
|
|
147
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
148
|
+
print(f"TRACEABILITY_FAIL: cannot read manifest: {exc}", file=sys.stderr)
|
|
149
|
+
return 2
|
|
150
|
+
if not isinstance(data, dict):
|
|
151
|
+
print("TRACEABILITY_FAIL: manifest root must be an object", file=sys.stderr)
|
|
152
|
+
return 2
|
|
153
|
+
errors = check(data, args.stage)
|
|
154
|
+
result = {"status": "passed" if not errors else "failed", "stage": args.stage, "errors": errors}
|
|
155
|
+
print(json.dumps(result, ensure_ascii=False))
|
|
156
|
+
return 0 if not errors else 1
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
raise SystemExit(main())
|
|
@@ -324,6 +324,7 @@ module WorkflowStateValidator
|
|
|
324
324
|
validate_frontend_tech_spec_gate(issues, step, data)
|
|
325
325
|
validate_api_supplement_gate(issues, step, data)
|
|
326
326
|
validate_integration_merge_gate(issues, step, data)
|
|
327
|
+
validate_change_plan_gate(issues, step, data)
|
|
327
328
|
validate_verification_gate(issues, step, data)
|
|
328
329
|
rescue JSON::ParserError => e
|
|
329
330
|
issues << issue("error", "invalid_meta_json", e.message, step: step["id"], path: path)
|
|
@@ -423,6 +424,10 @@ module WorkflowStateValidator
|
|
|
423
424
|
|
|
424
425
|
issues << issue("error", "baseline_blocking_counter_nonzero", "Requirement baseline has a non-zero blocking counter", step: step["id"], field: field, count: data[field])
|
|
425
426
|
end
|
|
427
|
+
|
|
428
|
+
if data["traceability_status"] != "passed"
|
|
429
|
+
issues << issue("error", "baseline_traceability_not_passed", "Requirement traceability must pass before technical design", step: step["id"], status: data["traceability_status"])
|
|
430
|
+
end
|
|
426
431
|
end
|
|
427
432
|
|
|
428
433
|
def validate_api_contract_gate(issues, step, data)
|
|
@@ -436,6 +441,10 @@ module WorkflowStateValidator
|
|
|
436
441
|
issues << issue("error", "api_contract_pending_items", "API contract has unresolved pending contract items", step: step["id"], count: data["pending_contract_count"])
|
|
437
442
|
end
|
|
438
443
|
|
|
444
|
+
if data["traceability_status"] != "passed"
|
|
445
|
+
issues << issue("error", "api_contract_traceability_not_passed", "API contract must retain complete requirement traceability", step: step["id"], status: data["traceability_status"])
|
|
446
|
+
end
|
|
447
|
+
|
|
439
448
|
api_required = data["api_required"]
|
|
440
449
|
endpoint_count = data["endpoint_count"].to_i
|
|
441
450
|
if api_required == false && endpoint_count.positive?
|
|
@@ -515,6 +524,10 @@ module WorkflowStateValidator
|
|
|
515
524
|
issues << issue("error", "merge_gate_not_passed", "Integration merge gate status is not passed", step: step["id"], status: data["merge_gate_status"])
|
|
516
525
|
end
|
|
517
526
|
|
|
527
|
+
if data["traceability_status"] != "passed"
|
|
528
|
+
issues << issue("error", "integration_traceability_not_passed", "Integration gate requires complete requirement-to-design traceability", step: step["id"], status: data["traceability_status"])
|
|
529
|
+
end
|
|
530
|
+
|
|
518
531
|
%w[
|
|
519
532
|
contract_backend_mismatch_count
|
|
520
533
|
contract_frontend_mismatch_count
|
|
@@ -527,6 +540,20 @@ module WorkflowStateValidator
|
|
|
527
540
|
end
|
|
528
541
|
end
|
|
529
542
|
|
|
543
|
+
def validate_change_plan_gate(issues, step, data)
|
|
544
|
+
return unless step["id"] == "change-plan"
|
|
545
|
+
|
|
546
|
+
if data["traceability_status"] != "passed"
|
|
547
|
+
issues << issue("error", "change_plan_traceability_not_passed", "Change plan requires complete requirement-to-task traceability", step: step["id"], status: data["traceability_status"])
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
%w[uncovered_requirement_count orphan_task_count].each do |field|
|
|
551
|
+
next if data[field].to_i.zero?
|
|
552
|
+
|
|
553
|
+
issues << issue("error", "change_plan_traceability_counter_nonzero", "Change plan has unresolved traceability coverage", step: step["id"], field: field, count: data[field])
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
|
|
530
557
|
def validate_verification_gate(issues, step, data)
|
|
531
558
|
return unless step["id"] == "verify"
|
|
532
559
|
|
package/plugins/devflow-core/skills/workflow-orchestrator/templates/feature-development.yaml
CHANGED
|
@@ -126,6 +126,7 @@ checkpoints:
|
|
|
126
126
|
producing_step: requirement-baseline
|
|
127
127
|
required_outputs:
|
|
128
128
|
- ".planning/requirements/baseline.md"
|
|
129
|
+
- ".planning/requirements/{feature}/traceability.json"
|
|
129
130
|
required_meta_output: ".planning/requirements/baseline.meta.json"
|
|
130
131
|
required_meta_fields:
|
|
131
132
|
- review_status
|
|
@@ -138,6 +139,7 @@ checkpoints:
|
|
|
138
139
|
- code_reality_missing_count
|
|
139
140
|
- current_feature
|
|
140
141
|
- depends_on_count
|
|
142
|
+
- traceability_status
|
|
141
143
|
summary_fields:
|
|
142
144
|
- fp_count
|
|
143
145
|
- br_count
|
|
@@ -146,9 +148,10 @@ checkpoints:
|
|
|
146
148
|
- critical_image_missing_count
|
|
147
149
|
- challenge_pending_count
|
|
148
150
|
- code_reality_missing_count
|
|
151
|
+
- traceability_status
|
|
149
152
|
- key_decisions
|
|
150
153
|
- risks
|
|
151
|
-
prompt: "
|
|
154
|
+
prompt: "确认需求基线和需求追溯清单均完整;每个 REQ 都有六维验收定义,并允许进入 UI/功能拆解和技术方案阶段。"
|
|
152
155
|
confirm_options:
|
|
153
156
|
- approve
|
|
154
157
|
- needs_fix
|
|
@@ -196,6 +199,7 @@ checkpoints:
|
|
|
196
199
|
- contract_frontend_mismatch_count
|
|
197
200
|
- unhandled_api_supplement_count
|
|
198
201
|
- blocking_issue_count
|
|
202
|
+
- traceability_status
|
|
199
203
|
summary_fields:
|
|
200
204
|
- merge_gate_status
|
|
201
205
|
- api_required
|
|
@@ -203,7 +207,8 @@ checkpoints:
|
|
|
203
207
|
- contract_frontend_mismatch_count
|
|
204
208
|
- unhandled_api_supplement_count
|
|
205
209
|
- blocking_issue_count
|
|
206
|
-
|
|
210
|
+
- traceability_status
|
|
211
|
+
prompt: "确认 API 契约、后端方案、前端调用三方一致,且所有 REQ 都已有设计覆盖,并允许进入变更清单。"
|
|
207
212
|
confirm_options:
|
|
208
213
|
- approve
|
|
209
214
|
- needs_fix
|
|
@@ -215,13 +220,19 @@ checkpoints:
|
|
|
215
220
|
producing_step: change-plan
|
|
216
221
|
required_outputs:
|
|
217
222
|
- ".planning/features/{feature}/变更清单.md"
|
|
223
|
+
- ".planning/features/{feature}/变更清单.meta.json"
|
|
224
|
+
required_meta_output: ".planning/features/{feature}/变更清单.meta.json"
|
|
225
|
+
required_meta_fields:
|
|
226
|
+
- traceability_status
|
|
227
|
+
- uncovered_requirement_count
|
|
228
|
+
- orphan_task_count
|
|
218
229
|
summary_fields:
|
|
219
230
|
- task_count
|
|
220
231
|
- ddl_count
|
|
221
232
|
- migration_steps
|
|
222
233
|
- verification_steps
|
|
223
234
|
- risks
|
|
224
|
-
prompt: "确认变更清单中的 DDL
|
|
235
|
+
prompt: "确认变更清单中的 DDL、代码任务、上线步骤和验收标准可执行;每项 REQ 都有任务、每项任务都有 REQ 来源,并允许进入代码执行。"
|
|
225
236
|
confirm_options:
|
|
226
237
|
- approve
|
|
227
238
|
- needs_fix
|
|
@@ -379,6 +390,9 @@ steps:
|
|
|
379
390
|
- path: ".planning/requirements/baseline.md"
|
|
380
391
|
type: requirement-baseline
|
|
381
392
|
required: true
|
|
393
|
+
- path: ".planning/requirements/{feature}/traceability.json"
|
|
394
|
+
type: requirement-traceability
|
|
395
|
+
required: true
|
|
382
396
|
meta_output:
|
|
383
397
|
path: ".planning/requirements/baseline.meta.json"
|
|
384
398
|
fields:
|
|
@@ -395,6 +409,7 @@ steps:
|
|
|
395
409
|
pending_items: { type: array, required: false }
|
|
396
410
|
risks: { type: array, required: false }
|
|
397
411
|
key_decisions: { type: array, max: 5, required: false }
|
|
412
|
+
traceability_status: { type: string, required: true }
|
|
398
413
|
checkpoint: CP-1
|
|
399
414
|
|
|
400
415
|
- id: api-contract
|
|
@@ -420,6 +435,7 @@ steps:
|
|
|
420
435
|
breaking_change_count: { type: integer, required: true }
|
|
421
436
|
permission_rule_count: { type: integer, required: true }
|
|
422
437
|
error_case_count: { type: integer, required: true }
|
|
438
|
+
traceability_status: { type: string, required: true }
|
|
423
439
|
checkpoint: CP-API
|
|
424
440
|
|
|
425
441
|
- id: ui-spec
|
|
@@ -587,6 +603,12 @@ steps:
|
|
|
587
603
|
- path: ".planning/features/{feature}/变更清单.md"
|
|
588
604
|
type: change-plan
|
|
589
605
|
required: true
|
|
606
|
+
meta_output:
|
|
607
|
+
path: ".planning/features/{feature}/变更清单.meta.json"
|
|
608
|
+
fields:
|
|
609
|
+
traceability_status: { type: string, required: true }
|
|
610
|
+
uncovered_requirement_count: { type: integer, required: true }
|
|
611
|
+
orphan_task_count: { type: integer, required: true }
|
|
590
612
|
checkpoint: CP-3
|
|
591
613
|
|
|
592
614
|
- id: phase-structure
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"feature": "{feature}",
|
|
4
|
+
"requirements": [
|
|
5
|
+
{
|
|
6
|
+
"id": "REQ-001",
|
|
7
|
+
"title": "原子且可验收的需求",
|
|
8
|
+
"status": "confirmed",
|
|
9
|
+
"scope": "in",
|
|
10
|
+
"dimensions": {
|
|
11
|
+
"input": "输入、前置条件与参数",
|
|
12
|
+
"output": "输出、持久化或界面结果",
|
|
13
|
+
"behavior": "正常业务语义",
|
|
14
|
+
"orchestration": "遍历、触发、顺序、并发或调度;无则写 N/A",
|
|
15
|
+
"failure": "异常、兼容、幂等、资源限制;无则写 N/A",
|
|
16
|
+
"verification": "可执行的验收证据"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"designs": [],
|
|
21
|
+
"tasks": [],
|
|
22
|
+
"verification": []
|
|
23
|
+
}
|
|
@@ -27,11 +27,14 @@ description: "Use after requirement-baseline, before dual-track fork. Defines sh
|
|
|
27
27
|
- `.planning/features/{功能名}/api-contract.md`
|
|
28
28
|
- `.planning/features/{功能名}/api-contract.meta.json`
|
|
29
29
|
|
|
30
|
+
The meta file must also set `traceability_status: "passed"` only after every API design has been written to `traceability.json.designs` and `traceability_check.py --stage design` succeeds; otherwise set `failed` and do not advance.
|
|
31
|
+
|
|
30
32
|
## 输入
|
|
31
33
|
|
|
32
34
|
- `.planning/requirements/baseline.md` — 需求基线(FP/BR/BC)
|
|
33
35
|
- `.planning/navigation/INDEX.md` 和相关 codebase navigation — 粗定位已有接口
|
|
34
36
|
- `.planning/code-index/`(如存在)— 查询已有 route/handler/API symbol 与 `routes_to` / `calls` 关系,确认接口复用和冲突
|
|
37
|
+
- `.planning/requirements/{feature}/traceability.json` — 每个接口必须回指它的 `REQ-*`,不能只写 FP 标题
|
|
35
38
|
|
|
36
39
|
使用 code-index 时,必须回读 `evidence_path:evidence_line` 附近源码后再把既有接口写入契约;不得只凭 Markdown navigation 或 relation 摘要判断。
|
|
37
40
|
|
|
@@ -39,7 +42,7 @@ description: "Use after requirement-baseline, before dual-track fork. Defines sh
|
|
|
39
42
|
|
|
40
43
|
1. **只定义接口契约,不谈实现细节** — 不写数据表结构、不写具体代码、不写业务流程内部逻辑
|
|
41
44
|
2. **分叉后不变更** — 一旦确定,Track A/B 都基于此契约工作。变更走 `api-supplement` 异常路径
|
|
42
|
-
3. **接口签名必须完整** — 包括 endpoint、method
|
|
45
|
+
3. **接口签名必须完整** — 包括 endpoint、method、请求参数(含类型约束)、响应结构(含状态码)、错误契约与行为语义(过滤、排序、权限、幂等/兼容规则)
|
|
43
46
|
4. **数据模型共享** — 核心业务实体定义在这里,前后端共用一个数据模型
|
|
44
47
|
5. **切片对齐** — 若 baseline 包含 `SL-*`,先读取 `skill://workflow-pipeline/references/slice-safety-guide.md`,只为当前执行切片定义 API。后置切片接口不得提前进入契约。
|
|
45
48
|
6. **既有接口优先查事实索引** — 当 `.planning/code-index/` 存在时,先用 Symbol/Relation 定位候选接口、调用方和处理器,再读取源码证据;缺失或过期时再按 navigation 和 `rg` 兜底。
|
|
@@ -42,6 +42,10 @@ allowed-tools:
|
|
|
42
42
|
|
|
43
43
|
# 后端技术方案生成器
|
|
44
44
|
|
|
45
|
+
## 需求追溯硬约束
|
|
46
|
+
|
|
47
|
+
读取 `.planning/requirements/{feature}/traceability.json`。每个设计章节必须列 `覆盖 REQ: REQ-*`;定时任务、批处理、导入和通知至少拆为“编排(扫描/触发/锁/重试)”与“业务算法(判定/计算/写入)”两个可独立验证的小节。不得把遍历全部活跃组织、发送通知、文件解析等编排事实藏在算法描述中。
|
|
48
|
+
|
|
45
49
|
根据需求基线和项目现有代码,生成聚焦于影响分析的后端技术方案。
|
|
46
50
|
|
|
47
51
|
## 核心原则
|
|
@@ -34,6 +34,7 @@ description: "Use after both Track A (api-call-guide) and Track B (frontend-tech
|
|
|
34
34
|
{
|
|
35
35
|
"api_required": true,
|
|
36
36
|
"merge_gate_status": "passed",
|
|
37
|
+
"traceability_status": "passed",
|
|
37
38
|
"contract_backend_mismatch_count": 0,
|
|
38
39
|
"contract_frontend_mismatch_count": 0,
|
|
39
40
|
"unhandled_api_supplement_count": 0,
|
|
@@ -50,6 +51,7 @@ description: "Use after both Track A (api-call-guide) and Track B (frontend-tech
|
|
|
50
51
|
- `.planning/features/{功能名}/后端技术方案.md` — Track A 产出的后端方案
|
|
51
52
|
- `.planning/features/{功能名}/api-supplement-requests.md` —(可选)Track B 提交的 API 补充需求
|
|
52
53
|
- `.planning/code-index/`(如存在)— 用于抽查 API route/handler、frontend API calls、imports/calls 关系,并回读源码 evidence
|
|
54
|
+
- `.planning/requirements/{feature}/traceability.json` — 原子 REQ 的唯一追溯源
|
|
53
55
|
|
|
54
56
|
## 核心原则
|
|
55
57
|
|
|
@@ -107,6 +109,13 @@ description: "Use after both Track A (api-call-guide) and Track B (frontend-tech
|
|
|
107
109
|
- 可以用已有接口拼吗?
|
|
108
110
|
- 必须新增的对 Track A 影响多大?
|
|
109
111
|
|
|
112
|
+
### Step 5:需求→设计闭环(不可跳过)
|
|
113
|
+
|
|
114
|
+
1. 将后端、前端、UI、API 契约中的设计项写入 `traceability.json.designs`,每项以 `requirements: ["REQ-…"]` 回指来源。
|
|
115
|
+
2. 所有 API 类型设计必须在 `contract` 中同时写 `request`、`response`、`errors`、`behavior`;禁止只对齐响应字段。
|
|
116
|
+
3. 执行 `python3 <workflow-orchestrator>/scripts/traceability_check.py --file .planning/requirements/{feature}/traceability.json --stage design`。
|
|
117
|
+
4. 结果不为 passed 时,`merge_gate_status` 必须为 failed,`blocking_issue_count` 必须大于 0,回到对应方案修复;不得进入变更清单。
|
|
118
|
+
|
|
110
119
|
## 产出模板
|
|
111
120
|
|
|
112
121
|
```
|
|
@@ -169,4 +178,5 @@ description: "Use after both Track A (api-call-guide) and Track B (frontend-tech
|
|
|
169
178
|
- [ ] 验证结论已写入报告
|
|
170
179
|
- [ ] meta.json 中 `merge_gate_status` 为 `passed`
|
|
171
180
|
- [ ] meta.json 中所有 mismatch / supplement / blocking 计数均为 0
|
|
181
|
+
- [ ] `traceability_check.py --stage design` 通过,且 meta.json 的 `traceability_status` 为 `passed`
|
|
172
182
|
- [ ] 无 API 变更时生成 no-op meta,`api_required` 为 false,`merge_gate_status` 为 `skipped`
|
|
@@ -58,6 +58,7 @@ description: "Use after workflow-integration-verify has passed or produced an ap
|
|
|
58
58
|
5. 读取功能点梳理(如存在)→ 确认 FP 覆盖完整性
|
|
59
59
|
6. 读取 baseline.md 的审查结论和风险标记
|
|
60
60
|
7. 若输入包含 `SL-*` 切片计划,读取 `skill://workflow-pipeline/references/slice-safety-guide.md`,只生成当前执行切片的任务。后置切片只能写入风险/后续范围,不得生成实现任务。
|
|
61
|
+
8. 读取 `.planning/requirements/{feature}/traceability.json`;它是任务覆盖判断的唯一 REQ 清单,不能仅凭技术方案标题推断覆盖。
|
|
61
62
|
|
|
62
63
|
### 第二阶段:扫描项目上下文
|
|
63
64
|
|
|
@@ -70,6 +71,8 @@ description: "Use after workflow-integration-verify has passed or produced an ap
|
|
|
70
71
|
|
|
71
72
|
输出到 `.planning/features/{feature}/变更清单.md`。
|
|
72
73
|
|
|
74
|
+
同时输出 `.planning/features/{feature}/变更清单.meta.json`。只有 `traceability_check.py --stage change-plan` 成功时才允许写入 `traceability_status: "passed"`;`uncovered_requirement_count` 和 `orphan_task_count` 必须为 0。
|
|
75
|
+
|
|
73
76
|
## 输出结构
|
|
74
77
|
|
|
75
78
|
```markdown
|
|
@@ -109,6 +112,7 @@ description: "Use after workflow-integration-verify has passed or produced an ap
|
|
|
109
112
|
- **参考实现**:`path/to/similar/file.py:line`(有则填写)
|
|
110
113
|
- **Code Index 证据**:相关 symbol/relation/evidence(有 `.planning/code-index/` 时填写;无则写"未使用:原因")
|
|
111
114
|
- **需求来源**:FP-001 / BR-001 / 技术方案第X章
|
|
115
|
+
- **REQ 覆盖**:`REQ-001`(必须是 traceability.json 中的原子 ID;无来源任务必须显式标为获批技术债)
|
|
112
116
|
- **Slice**:SL-001
|
|
113
117
|
- **适用规范**:`.context/spec/backend/service.md` 等(有则填写)
|
|
114
118
|
- **相关经验教训**:lesson ID 或"无"
|
|
@@ -119,6 +123,7 @@ description: "Use after workflow-integration-verify has passed or produced an ap
|
|
|
119
123
|
- **完成标准**:(每条必须使用 `- [ ]` checkbox 格式,可被 code_review 门禁自动验证)
|
|
120
124
|
- [ ] 具体可验证的检查项(可映射到代码中具体元素)
|
|
121
125
|
- [ ] 另一条检查项(可映射到验证命令)
|
|
126
|
+
- **六维验收**:输入、输出、行为、编排、失败、验证各写一条可验证结论;算法与遍历/调度等编排必须分列,不能相互替代。
|
|
122
127
|
- **验证命令**:`pytest tests/test_xxx.py -k test_yyy`
|
|
123
128
|
|
|
124
129
|
## 四、配置变更
|
|
@@ -158,3 +163,6 @@ graph TD
|
|
|
158
163
|
- [ ] 若 `.planning/code-index/` 存在,涉及已有代码的任务已标注相关 symbol/relation/evidence 或说明未使用原因
|
|
159
164
|
- [ ] 每个任务包含审查要求、适用规范、需求来源字段(至少标注"无")
|
|
160
165
|
- [ ] 文件路径与技术方案一致
|
|
166
|
+
- [ ] 变更清单任务同步写入 `traceability.json.tasks`,每项包含 `id`、`requirements` 和六维 `acceptance`
|
|
167
|
+
- [ ] 执行 `traceability_check.py --stage change-plan` 通过;没有漏 REQ,也没有无 REQ 来源的任务
|
|
168
|
+
- [ ] 输出 `变更清单.meta.json`,至少包含 `traceability_status: passed`、`uncovered_requirement_count: 0`、`orphan_task_count: 0`
|
|
@@ -107,6 +107,15 @@ Always create or update:
|
|
|
107
107
|
|
|
108
108
|
- `.planning/requirements/baseline.md` — unified requirement baseline
|
|
109
109
|
- `.planning/requirements/baseline.meta.json` — structured metadata. Schema defined in `feature-development.yaml` under `steps[requirement-baseline].meta_output.fields`: `review_status`, `fp_count`, `br_count`, `bc_count`, `blocking_pending_count`, `critical_image_missing_count`, `challenge_pending_count`, `code_reality_missing_count`, `current_feature`, `depends_on_count`, plus optional `pending_items`, `risks`, and `key_decisions`.
|
|
110
|
+
- `.planning/requirements/{feature}/traceability.json` — copy the orchestrator `templates/traceability.json` schema and create one atomic `REQ-*` record for every confirmed in-scope requirement. Every record must define `input`、`output`、`behavior`、`orchestration`、`failure`、`verification`; use `N/A` only with a reason when a dimension truly does not apply.
|
|
111
|
+
|
|
112
|
+
Before the CP-1 result is reported, run:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python3 <workflow-orchestrator>/scripts/traceability_check.py --file .planning/requirements/{feature}/traceability.json --stage baseline
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Set `baseline.meta.json.traceability_status` to `passed` only when that command succeeds. A failed traceability check is a blocking baseline defect, not a deferred documentation suggestion.
|
|
110
119
|
|
|
111
120
|
Do not create by default:
|
|
112
121
|
|