cc-devflow 4.5.1 → 4.5.3
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/.claude/skills/cc-act/CHANGELOG.md +27 -0
- package/.claude/skills/cc-act/PLAYBOOK.md +32 -1
- package/.claude/skills/cc-act/SKILL.md +53 -7
- package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +35 -1
- package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +10 -1
- package/.claude/skills/cc-act/references/closure-contract.md +11 -0
- package/.claude/skills/cc-act/scripts/cc-act-common.sh +32 -1
- package/.claude/skills/cc-act/scripts/render-pr-brief.sh +130 -0
- package/.claude/skills/cc-act/scripts/verify-act-gate.sh +23 -1
- package/.claude/skills/cc-check/CHANGELOG.md +26 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +128 -1
- package/.claude/skills/cc-check/SKILL.md +147 -7
- package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +164 -1
- package/.claude/skills/cc-check/references/gate-contract.md +11 -0
- package/.claude/skills/cc-check/references/review-contract.md +104 -0
- package/.claude/skills/cc-check/scripts/render-report-card.js +209 -5
- package/.claude/skills/cc-check/scripts/verify-gate.sh +28 -0
- package/.claude/skills/cc-do/CHANGELOG.md +12 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +14 -9
- package/.claude/skills/cc-do/SKILL.md +24 -13
- package/.claude/skills/cc-do/references/execution-recovery.md +16 -5
- package/.claude/skills/cc-do/scripts/verify-task-gates.sh +19 -6
- package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +14 -2
- package/.claude/skills/cc-investigate/CHANGELOG.md +31 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +124 -8
- package/.claude/skills/cc-investigate/SKILL.md +252 -17
- package/.claude/skills/cc-investigate/assets/ANALYSIS_TEMPLATE.md +112 -3
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +17 -5
- package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +141 -1
- package/.claude/skills/cc-investigate/references/investigation-contract.md +192 -0
- package/.claude/skills/cc-plan/CHANGELOG.md +26 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +18 -6
- package/.claude/skills/cc-plan/SKILL.md +72 -34
- package/.claude/skills/cc-plan/assets/DESIGN_TEMPLATE.md +30 -3
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +28 -0
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +46 -1
- package/.claude/skills/cc-plan/assets/TINY_DESIGN_TEMPLATE.md +24 -0
- package/.claude/skills/cc-plan/references/planning-contract.md +18 -4
- package/.claude/skills/cc-roadmap/CHANGELOG.md +14 -0
- package/.claude/skills/cc-roadmap/PLAYBOOK.md +10 -7
- package/.claude/skills/cc-roadmap/SKILL.md +43 -23
- package/.claude/skills/cc-roadmap/assets/BACKLOG_TEMPLATE.md +10 -0
- package/.claude/skills/cc-roadmap/assets/ROADMAP_TEMPLATE.md +15 -0
- package/.claude/skills/cc-roadmap/assets/TRACKING_TEMPLATE.json +1 -1
- package/.claude/skills/cc-roadmap/references/roadmap-dialogue.md +11 -7
- package/.claude/skills/cc-simplify/CHANGELOG.md +21 -0
- package/.claude/skills/cc-simplify/SKILL.md +264 -35
- package/.claude/skills/cc-spec-init/CHANGELOG.md +6 -0
- package/.claude/skills/cc-spec-init/SKILL.md +14 -1
- package/CHANGELOG.md +37 -0
- package/README.md +10 -2
- package/README.zh-CN.md +10 -2
- package/docs/examples/example-bindings.json +7 -7
- package/docs/examples/full-design-blocked/BACKLOG.md +1 -1
- package/docs/examples/full-design-blocked/README.md +1 -1
- package/docs/examples/full-design-blocked/ROADMAP.md +1 -1
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +1 -1
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/tasks.md +1 -1
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +140 -3
- package/docs/examples/full-design-blocked/roadmap-tracking.json +1 -1
- package/docs/examples/local-handoff/BACKLOG.md +1 -1
- package/docs/examples/local-handoff/README.md +1 -1
- package/docs/examples/local-handoff/ROADMAP.md +1 -1
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +1 -1
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/tasks.md +1 -1
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +92 -0
- package/docs/examples/local-handoff/roadmap-tracking.json +1 -1
- package/docs/examples/pdca-loop/BACKLOG.md +1 -1
- package/docs/examples/pdca-loop/README.md +1 -1
- package/docs/examples/pdca-loop/ROADMAP.md +1 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +20 -0
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +1 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +2 -2
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/tasks.md +1 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +92 -0
- package/docs/examples/pdca-loop/roadmap-tracking.json +1 -1
- package/docs/skill-strategy-audit.md +48 -0
- package/lib/skill-runtime/__tests__/runtime.integration.test.js +19 -1
- package/lib/skill-runtime/review.js +64 -1
- package/lib/skill-runtime/schemas.js +161 -4
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
每个 reviewer 结果至少说明:
|
|
23
23
|
|
|
24
|
+
- reviewPacket
|
|
24
25
|
- status
|
|
25
26
|
- summary
|
|
26
27
|
- evidence
|
|
@@ -30,16 +31,119 @@
|
|
|
30
31
|
|
|
31
32
|
- severity
|
|
32
33
|
- confidence
|
|
34
|
+
- confidenceScore
|
|
33
35
|
- source
|
|
34
36
|
- summary
|
|
35
37
|
- evidence
|
|
36
38
|
- action
|
|
39
|
+
- triageStatus
|
|
40
|
+
- fingerprint
|
|
41
|
+
- displayTier
|
|
42
|
+
- suppressionReason
|
|
43
|
+
|
|
44
|
+
## Review Packet
|
|
45
|
+
|
|
46
|
+
Review 不能依赖聊天记忆。每个 task-level review 和 requirement-level diff review 至少记录:
|
|
47
|
+
|
|
48
|
+
- `baseSha`:被审查范围的起点
|
|
49
|
+
- `headSha`:被审查范围的终点
|
|
50
|
+
- `requirements`:对应的 plan、task、analysis 或 spec 路径
|
|
51
|
+
- `implemented`:实现者声称完成的内容
|
|
52
|
+
- `reviewerContext`:reviewer 实际拿到的上下文摘要
|
|
53
|
+
|
|
54
|
+
缺 `baseSha` / `headSha` 时,review 只能算 `blocked` 或 `skipped`,不能支撑 `pass`。
|
|
55
|
+
|
|
56
|
+
## Review Freshness
|
|
57
|
+
|
|
58
|
+
Review 必须绑定当前被交付的 commit,而不是绑定聊天记忆。
|
|
59
|
+
|
|
60
|
+
每份 requirement-level review 至少记录:
|
|
61
|
+
|
|
62
|
+
- `review.freshness.status`:`fresh` / `stale` / `unknown` / `not-applicable`
|
|
63
|
+
- `review.freshness.reviewedCommit`
|
|
64
|
+
- `review.freshness.currentCommit`
|
|
65
|
+
- `review.freshness.commitsSinceReview`
|
|
66
|
+
- `review.freshness.staleReason`
|
|
67
|
+
- `review.qualityScore`:0-10,可空但空值不能支撑高置信 pass
|
|
68
|
+
|
|
69
|
+
`status=stale`、`status=unknown` 且没有解释,或 `commitsSinceReview > 0` 且未重审,都会阻塞 `pass`。
|
|
70
|
+
|
|
71
|
+
## Specialist Facets
|
|
72
|
+
|
|
73
|
+
`review.specialistReviews[]` 用来记录按风险覆盖的审查面,不要求每次都派发独立 reviewer,但要求边界说清:
|
|
74
|
+
|
|
75
|
+
- `testing`:负路径、边界条件、隔离性、flaky 风险、回归测试质量
|
|
76
|
+
- `security`:trust boundary、shell / SQL / secret / auth 风险
|
|
77
|
+
- `performance`:热路径、批量、缓存、N+1、资源泄漏
|
|
78
|
+
- `api-contract`:输入输出、状态枚举、兼容面、错误语义
|
|
79
|
+
- `data-migration`:schema、回滚、幂等、历史数据
|
|
80
|
+
- `design`:UI / UX / visual consistency 和可用性
|
|
81
|
+
|
|
82
|
+
没有相关风险时写 `status=skipped` 和 `skipReason`;有风险却缺 facet 时,至少是 review gap。
|
|
83
|
+
|
|
84
|
+
## Finding Triage
|
|
85
|
+
|
|
86
|
+
Review finding 不只是“发现过”,必须有处置结果:
|
|
87
|
+
|
|
88
|
+
| triageStatus | 什么时候用 |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `accepted-fixed` | finding 正确,已修复,并有验证证据 |
|
|
91
|
+
| `rejected-with-evidence` | finding 不适用,已有代码 / 测试 / 契约证据支撑 |
|
|
92
|
+
| `deferred-minor` | minor,不阻塞本次交付,已写入 follow-up |
|
|
93
|
+
| `clarification-needed` | finding 不清楚,需要用户或 reviewer 澄清 |
|
|
94
|
+
|
|
95
|
+
`critical` / `important` finding 不能用 `deferred-minor`。任何 `clarification-needed` 都会阻塞 `pass`。
|
|
96
|
+
|
|
97
|
+
## QA Test Review Facts
|
|
98
|
+
|
|
99
|
+
Review 必须判断测试是否证明行为:
|
|
100
|
+
|
|
101
|
+
- 反馈环是否可信:速度、确定性、信号锋利度、复现率是否足够支撑结论
|
|
102
|
+
- bugfix 是否复现并覆盖了用户描述的原始症状,而不是附近的另一个失败
|
|
103
|
+
- expected / actual / reproduction steps 是否能让 reviewer 独立复现或判断缺件
|
|
104
|
+
- 回归测试是否有 red/green 证据
|
|
105
|
+
- red 是否因为目标行为缺失而失败
|
|
106
|
+
- green 是否包含 targeted test 和必要的 broader gate
|
|
107
|
+
- 测试是否通过公共接口覆盖行为
|
|
108
|
+
- mock 是否只停在系统边界,且没有断言 mock 本身或内部调用顺序
|
|
109
|
+
- 生产代码是否新增 test-only API
|
|
110
|
+
- integration / contract test 是否比复杂 mock 更直接
|
|
111
|
+
- 如果没有正确测试 seam,是否记录了架构 follow-up,而不是造易碎测试
|
|
112
|
+
- coverage audit 是否映射真实 codepath / user flow / error state / edge case
|
|
113
|
+
- UI 或用户路径变更是否有 browser evidence、截图、console 结果,或明确 skip reason
|
|
114
|
+
|
|
115
|
+
## Durable Follow-Up Facts
|
|
116
|
+
|
|
117
|
+
Review 产生的 QA issue 或 follow-up 必须可长期执行:
|
|
118
|
+
|
|
119
|
+
- 用领域语言描述用户或系统行为,不把当前文件路径 / 行号当成唯一真相
|
|
120
|
+
- 写清 current behavior、desired behavior、key interfaces、acceptance criteria、out of scope
|
|
121
|
+
- 独立行为拆成独立条目;有依赖关系时写明顺序
|
|
122
|
+
- `deferred-minor` 只能用于不阻塞本次交付的 minor 项,并且必须进入 `cc-act` follow-up writeback
|
|
123
|
+
|
|
124
|
+
## Failure Ownership
|
|
125
|
+
|
|
126
|
+
失败归属必须结构化写入 `runtime.failureOwnership[]`:
|
|
127
|
+
|
|
128
|
+
- `classification=in-branch`:当前分支引入
|
|
129
|
+
- `classification=pre-existing`:base branch 也能复现,必须有证据
|
|
130
|
+
- `classification=environment`:缺依赖、权限、服务、密钥或平台条件
|
|
131
|
+
- `classification=ambiguous`:归属不明,默认不能支撑 `pass`
|
|
132
|
+
|
|
133
|
+
不要把 pre-existing failure 当成当前分支失败,也不要把 ambiguous failure 当成环境问题。
|
|
37
134
|
|
|
38
135
|
## Gate Rules
|
|
39
136
|
|
|
40
137
|
- 任务级 review 缺证据,不能绿灯
|
|
41
138
|
- 需求级 diff review 在 strict 模式下缺失,至少是 `blocked`
|
|
42
139
|
- `important` / `critical` finding 未处理前,不算通过
|
|
140
|
+
- `important` / `critical` finding 缺 triageStatus,不算通过
|
|
141
|
+
- QA test quality 缺失且本次涉及行为变化,至少是 `blocked`
|
|
142
|
+
- 行为变更缺 `qa.feedbackLoop` / `qa.behaviorEvidence` 且没有明确例外,至少是 `blocked`
|
|
143
|
+
- bugfix 没有复现原始症状,也没有解释不可复现原因,不能通过
|
|
144
|
+
- review freshness 缺失、过期或与当前 head 不一致,不能绿灯
|
|
145
|
+
- UI / 用户路径变更缺 browser evidence 且无 skip reason,不能绿灯
|
|
146
|
+
- `runtime.failureOwnership` 仍有 `in-branch` 或 `ambiguous` 未解释失败,不能绿灯
|
|
43
147
|
- plan item 是 `PARTIAL` / `NOT_DONE` 且影响成功标准时,不能通过
|
|
44
148
|
- scope drift 没有解释清楚时,不能通过
|
|
45
149
|
- 文档漂移如果影响 reviewer / maintainer 接手,必须阻塞到 `cc-act` doc sync 或 reroute
|
|
@@ -68,6 +68,34 @@ function deriveVerdict(manifest, quickGates, strictGates, review) {
|
|
|
68
68
|
return 'fail';
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
if ([...quickGates, ...strictGates].some((gate) => ['blocked', 'pending'].includes(gate.status))) {
|
|
72
|
+
return 'blocked';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (review.qa?.status === 'fail') {
|
|
76
|
+
return 'fail';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (['blocked', 'pending'].includes(review.qa?.status)) {
|
|
80
|
+
return 'blocked';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (['fail'].includes(review.qa?.feedbackLoop?.status)) {
|
|
84
|
+
return 'fail';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (['blocked', 'pending'].includes(review.qa?.feedbackLoop?.status)) {
|
|
88
|
+
return 'blocked';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (['fail'].includes(review.qa?.behaviorEvidence?.status)) {
|
|
92
|
+
return 'fail';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (['blocked', 'pending'].includes(review.qa?.behaviorEvidence?.status)) {
|
|
96
|
+
return 'blocked';
|
|
97
|
+
}
|
|
98
|
+
|
|
71
99
|
if (review.status === 'blocked') {
|
|
72
100
|
return 'blocked';
|
|
73
101
|
}
|
|
@@ -76,6 +104,20 @@ function deriveVerdict(manifest, quickGates, strictGates, review) {
|
|
|
76
104
|
return 'fail';
|
|
77
105
|
}
|
|
78
106
|
|
|
107
|
+
const freshness = buildReviewFreshness(review).status;
|
|
108
|
+
if (review.status === 'pass' && !['fresh', 'not-applicable'].includes(freshness)) {
|
|
109
|
+
return 'blocked';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const openOwnedFailures = (review.runtime?.failureOwnership || []).some((item) => {
|
|
113
|
+
const classification = item.classification || '';
|
|
114
|
+
const status = item.status || 'open';
|
|
115
|
+
return ['in-branch', 'ambiguous'].includes(classification) && !['resolved', 'closed'].includes(status);
|
|
116
|
+
});
|
|
117
|
+
if (openOwnedFailures) {
|
|
118
|
+
return 'blocked';
|
|
119
|
+
}
|
|
120
|
+
|
|
79
121
|
return 'pass';
|
|
80
122
|
}
|
|
81
123
|
|
|
@@ -116,10 +158,160 @@ function buildSummary({ quickGates, strictGates, review, verdict }) {
|
|
|
116
158
|
].join(' ');
|
|
117
159
|
}
|
|
118
160
|
|
|
161
|
+
function claimFromGate(gate) {
|
|
162
|
+
const name = String(gate.name || '').toLowerCase();
|
|
163
|
+
if (/test|spec/.test(name)) {
|
|
164
|
+
return 'tests-pass';
|
|
165
|
+
}
|
|
166
|
+
if (/lint/.test(name)) {
|
|
167
|
+
return 'lint-clean';
|
|
168
|
+
}
|
|
169
|
+
if (/type/.test(name)) {
|
|
170
|
+
return 'typecheck-clean';
|
|
171
|
+
}
|
|
172
|
+
if (/build|compile/.test(name)) {
|
|
173
|
+
return 'build-succeeds';
|
|
174
|
+
}
|
|
175
|
+
return `gate-${gate.name || 'unknown'}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildClaimEvidence({ manifest, quickGates, strictGates, review }) {
|
|
179
|
+
const gateClaims = [...quickGates, ...strictGates].map((gate) => ({
|
|
180
|
+
claim: claimFromGate(gate),
|
|
181
|
+
requiredProof: 'fresh command output with exit status and key observation',
|
|
182
|
+
commandOrArtifact: gate.command || gate.name || '',
|
|
183
|
+
exitStatus: gate.exitStatus ?? null,
|
|
184
|
+
keyObservation: gate.summary || gate.details || '',
|
|
185
|
+
status: gate.status || 'blocked'
|
|
186
|
+
}));
|
|
187
|
+
|
|
188
|
+
const openTasks = (manifest.tasks || []).filter((task) => task.status !== 'done' && task.status !== 'completed');
|
|
189
|
+
gateClaims.push({
|
|
190
|
+
claim: 'requirements-met',
|
|
191
|
+
requiredProof: 'line-by-line planning/tasks.md and task-manifest.json checklist',
|
|
192
|
+
commandOrArtifact: 'planning/tasks.md + planning/task-manifest.json',
|
|
193
|
+
exitStatus: null,
|
|
194
|
+
keyObservation: openTasks.length === 0 ? 'no open task gaps recorded' : `${openTasks.length} open task gaps recorded`,
|
|
195
|
+
status: openTasks.length === 0 && review.status === 'pass' ? 'pass' : 'blocked'
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return [...(review.claimEvidence || []), ...gateClaims];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function buildQa(review) {
|
|
202
|
+
return {
|
|
203
|
+
status: review.qa?.status || 'skipped',
|
|
204
|
+
feedbackLoop: review.qa?.feedbackLoop || {
|
|
205
|
+
status: 'skipped',
|
|
206
|
+
mode: 'not-applicable',
|
|
207
|
+
commandOrArtifact: '',
|
|
208
|
+
speed: '',
|
|
209
|
+
determinism: '',
|
|
210
|
+
signalSharpness: '',
|
|
211
|
+
reproductionRate: '',
|
|
212
|
+
attempts: [],
|
|
213
|
+
blockedReason: 'not recorded'
|
|
214
|
+
},
|
|
215
|
+
behaviorEvidence: review.qa?.behaviorEvidence || {
|
|
216
|
+
status: 'skipped',
|
|
217
|
+
userFacingBoundary: '',
|
|
218
|
+
expectedBehavior: '',
|
|
219
|
+
actualBehavior: '',
|
|
220
|
+
reproductionSteps: [],
|
|
221
|
+
consistency: '',
|
|
222
|
+
domainLanguage: []
|
|
223
|
+
},
|
|
224
|
+
regressionProof: review.qa?.regressionProof || [],
|
|
225
|
+
testQuality: review.qa?.testQuality || [],
|
|
226
|
+
coverageAudit: review.qa?.coverageAudit || {
|
|
227
|
+
status: 'skipped',
|
|
228
|
+
coveragePct: null,
|
|
229
|
+
pathMap: [],
|
|
230
|
+
gaps: [],
|
|
231
|
+
testsAdded: [],
|
|
232
|
+
e2eRequired: false,
|
|
233
|
+
evalRequired: false,
|
|
234
|
+
qualityStars: ''
|
|
235
|
+
},
|
|
236
|
+
browserEvidence: review.qa?.browserEvidence || {
|
|
237
|
+
status: 'skipped',
|
|
238
|
+
mode: 'not-applicable',
|
|
239
|
+
affectedRoutes: [],
|
|
240
|
+
screenshots: [],
|
|
241
|
+
consoleErrors: [],
|
|
242
|
+
healthScore: null,
|
|
243
|
+
issues: [],
|
|
244
|
+
skipReason: 'not recorded'
|
|
245
|
+
},
|
|
246
|
+
architectureFollowUps: review.qa?.architectureFollowUps || [],
|
|
247
|
+
tddException: review.qa?.tddException || null
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function buildRuntime(review) {
|
|
252
|
+
const failureOwnership = review.runtime?.failureOwnership || [];
|
|
253
|
+
const hasOpenOwnedFailure = failureOwnership.some((item) => {
|
|
254
|
+
const classification = item.classification || '';
|
|
255
|
+
const status = item.status || 'open';
|
|
256
|
+
return ['in-branch', 'ambiguous'].includes(classification) && !['resolved', 'closed'].includes(status);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
status: review.runtime?.status || (hasOpenOwnedFailure ? 'blocked' : 'pass'),
|
|
261
|
+
failureOwnership
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function firstReviewHead(review) {
|
|
266
|
+
return [
|
|
267
|
+
review.taskReviews?.reviewPacket?.headSha,
|
|
268
|
+
review.diffReview?.reviewPacket?.headSha
|
|
269
|
+
].find((value) => typeof value === 'string' && value.length > 0) || '';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function buildReviewFreshness(review) {
|
|
273
|
+
if (review.freshness) {
|
|
274
|
+
return review.freshness;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const headSha = firstReviewHead(review);
|
|
278
|
+
if (!headSha) {
|
|
279
|
+
return {
|
|
280
|
+
status: 'unknown',
|
|
281
|
+
reviewedCommit: '',
|
|
282
|
+
currentCommit: '',
|
|
283
|
+
commitsSinceReview: null,
|
|
284
|
+
staleReason: 'review headSha is not recorded'
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
status: 'fresh',
|
|
290
|
+
reviewedCommit: headSha,
|
|
291
|
+
currentCommit: headSha,
|
|
292
|
+
commitsSinceReview: 0,
|
|
293
|
+
staleReason: ''
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function buildReview(review) {
|
|
298
|
+
return {
|
|
299
|
+
...review,
|
|
300
|
+
freshness: buildReviewFreshness(review),
|
|
301
|
+
qualityScore: review.qualityScore ?? null,
|
|
302
|
+
specialistReviews: review.specialistReviews || []
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function isFindingOpen(item) {
|
|
307
|
+
const status = item.status || item.triageStatus || '';
|
|
308
|
+
return !['resolved', 'accepted', 'informational', 'accepted-fixed', 'rejected-with-evidence', 'deferred-minor'].includes(status);
|
|
309
|
+
}
|
|
310
|
+
|
|
119
311
|
function summarizeOpenReviewFindings(findings = []) {
|
|
120
312
|
return findings
|
|
121
|
-
.filter(
|
|
122
|
-
.map((item) => `${item.source}: ${item.summary}`);
|
|
313
|
+
.filter(isFindingOpen)
|
|
314
|
+
.map((item) => `${item.source || 'review'}: ${item.summary || item.evidence || 'open finding'}`);
|
|
123
315
|
}
|
|
124
316
|
|
|
125
317
|
function collectBlockingFindings({ manifest, quickGates, strictGates, review }) {
|
|
@@ -132,8 +324,8 @@ function collectBlockingFindings({ manifest, quickGates, strictGates, review })
|
|
|
132
324
|
}
|
|
133
325
|
|
|
134
326
|
for (const gate of [...quickGates, ...strictGates]) {
|
|
135
|
-
if (
|
|
136
|
-
findings.push(`${gate.name}: ${gate.details}`);
|
|
327
|
+
if (['fail', 'blocked', 'pending'].includes(gate.status)) {
|
|
328
|
+
findings.push(`${gate.name}: ${gate.details || gate.summary || gate.status}`);
|
|
137
329
|
}
|
|
138
330
|
}
|
|
139
331
|
|
|
@@ -200,6 +392,15 @@ function main() {
|
|
|
200
392
|
blockingFindings
|
|
201
393
|
});
|
|
202
394
|
const specSignals = deriveSpecSignals(manifest, verdict, review);
|
|
395
|
+
const claimEvidence = buildClaimEvidence({
|
|
396
|
+
manifest,
|
|
397
|
+
quickGates,
|
|
398
|
+
strictGates,
|
|
399
|
+
review
|
|
400
|
+
});
|
|
401
|
+
const runtime = buildRuntime(review);
|
|
402
|
+
const qa = buildQa(review);
|
|
403
|
+
const reviewReport = buildReview(review);
|
|
203
404
|
|
|
204
405
|
const report = {
|
|
205
406
|
changeId: args.changeId,
|
|
@@ -214,9 +415,12 @@ function main() {
|
|
|
214
415
|
specAlignment: specSignals.specAlignment,
|
|
215
416
|
specDeltaVerified: specSignals.specDeltaVerified,
|
|
216
417
|
specSyncReady: specSignals.specSyncReady,
|
|
418
|
+
runtime,
|
|
419
|
+
claimEvidence,
|
|
420
|
+
qa,
|
|
217
421
|
quickGates,
|
|
218
422
|
strictGates,
|
|
219
|
-
review,
|
|
423
|
+
review: reviewReport,
|
|
220
424
|
blockingFindings,
|
|
221
425
|
gaps: manifest.spec?.newGaps || [],
|
|
222
426
|
reroute,
|
|
@@ -36,6 +36,10 @@ jq -e '
|
|
|
36
36
|
.summary and
|
|
37
37
|
.review and
|
|
38
38
|
.blockingFindings and
|
|
39
|
+
(.runtime and (.runtime | type == "object")) and
|
|
40
|
+
(.runtime.status | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
41
|
+
((.runtime.failureOwnership? // []) | type == "array") and
|
|
42
|
+
((.runtime.failureOwnership? // []) | all(.[]; (.classification? // "environment") | IN("in-branch", "pre-existing", "environment", "ambiguous"))) and
|
|
39
43
|
(.specAlignment? // "blocked") and
|
|
40
44
|
((.specDeltaVerified? // false) | type == "boolean") and
|
|
41
45
|
((.specSyncReady? // false) | type == "boolean") and
|
|
@@ -45,6 +49,30 @@ jq -e '
|
|
|
45
49
|
(.overall | IN("pass", "fail")) and
|
|
46
50
|
(.reroute | IN("none", "cc-do", "cc-investigate", "cc-plan")) and
|
|
47
51
|
(.review.status | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
52
|
+
((.review.freshness? // {"status":"unknown"}) | type == "object") and
|
|
53
|
+
((.review.freshness.status? // "unknown") | IN("fresh", "stale", "unknown", "not-applicable")) and
|
|
54
|
+
((.review.specialistReviews? // []) | type == "array") and
|
|
55
|
+
((.claimEvidence? // []) | type == "array") and
|
|
56
|
+
((.claimEvidence? // []) | all(.[];
|
|
57
|
+
(.claim and .requiredProof and .commandOrArtifact and .keyObservation and .status) and
|
|
58
|
+
(.status | IN("pass", "fail", "blocked", "skipped", "pending"))
|
|
59
|
+
)) and
|
|
60
|
+
((.qa? // {"status":"skipped"}) | type == "object") and
|
|
61
|
+
((.qa.status? // "skipped") | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
62
|
+
((.qa.coverageAudit? // {"status":"skipped"}) | type == "object") and
|
|
63
|
+
((.qa.coverageAudit.status? // "skipped") | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
64
|
+
((.qa.browserEvidence? // {"status":"skipped"}) | type == "object") and
|
|
65
|
+
((.qa.browserEvidence.status? // "skipped") | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
66
|
+
((.qa.feedbackLoop? // {"status":"skipped"}) | type == "object") and
|
|
67
|
+
((.qa.feedbackLoop.status? // "skipped") | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
68
|
+
((.qa.behaviorEvidence? // {"status":"skipped"}) | type == "object") and
|
|
69
|
+
((.qa.behaviorEvidence.status? // "skipped") | IN("pass", "fail", "blocked", "skipped", "pending")) and
|
|
70
|
+
((.qa.architectureFollowUps? // []) | type == "array") and
|
|
71
|
+
((.review.findings? // []) | all(.[]; ((.confidenceScore? // 7) | type == "number") and ((.displayTier? // "info") | IN("blocking", "warning", "info", "suppressed")))) and
|
|
72
|
+
((.verdict != "pass") or ((.review.freshness.status? // "unknown") | IN("fresh", "not-applicable"))) and
|
|
73
|
+
((.verdict != "pass") or ((.qa.feedbackLoop.status? // "skipped") | IN("pass", "skipped"))) and
|
|
74
|
+
((.verdict != "pass") or ((.qa.behaviorEvidence.status? // "skipped") | IN("pass", "skipped"))) and
|
|
75
|
+
((.verdict != "pass") or (((.runtime.failureOwnership? // []) | map(select(((.classification? // "") | IN("in-branch", "ambiguous")) and ((.status? // "open") | IN("open", "pending")))) | length) == 0)) and
|
|
48
76
|
((.verdict == "pass" and .reroute == "none") or (.verdict != "pass" and .reroute != "none"))
|
|
49
77
|
' "$REPORT" >/dev/null
|
|
50
78
|
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# CC-Do Skill Changelog
|
|
2
2
|
|
|
3
|
+
## v1.6.0 - 2026-04-28
|
|
4
|
+
|
|
5
|
+
- prohibit horizontal TDD execution by requiring one tracer bullet Red/Green/Refactor cycle per observable behavior
|
|
6
|
+
- add test fixture discipline so partial fixtures, type assertions, generated stubs, and mocks must preserve public seam behavior
|
|
7
|
+
- require checkpoints to record fixture risk when test data shortcuts could hide a seam or contract problem
|
|
8
|
+
|
|
9
|
+
## v1.5.3 - 2026-04-28
|
|
10
|
+
|
|
11
|
+
- require Red evidence to prove behavior through a public seam instead of private methods, internal call counts, or implementation-shaped tests
|
|
12
|
+
- add mock-boundary and test-quality gates to the TDD execution contract so internal collaborators are not mocked as fake proof
|
|
13
|
+
- allow `write-task-checkpoint.sh --tdd-json` and runtime checkpoint schema to preserve structured TDD evidence for recovery and review
|
|
14
|
+
|
|
3
15
|
## v1.5.2 - 2026-04-27
|
|
4
16
|
|
|
5
17
|
- require execution evidence that adds human-readable summaries to resolve the runtime output policy first
|
|
@@ -50,10 +50,12 @@
|
|
|
50
50
|
|
|
51
51
|
1. 先写失败测试,再运行到红。
|
|
52
52
|
2. 确认红灯是预期失败,不是测试写错、fixture 缺失或环境没接上。
|
|
53
|
-
3.
|
|
54
|
-
4.
|
|
55
|
-
5.
|
|
56
|
-
6.
|
|
53
|
+
3. 确认红灯通过公共 seam 证明行为缺失,而不是测私有函数、内部调用次数或临时结构。
|
|
54
|
+
4. 确认 mock 只发生在系统边界;内部协作者不 mock。
|
|
55
|
+
5. 只写让当前测试转绿的最小实现。
|
|
56
|
+
6. 绿后才允许重构。
|
|
57
|
+
7. 重构后必须保持绿。
|
|
58
|
+
8. 测试没先红过,或红灯不是公共 seam 上的行为失败,就不能宣称这次变更受 TDD 保护。
|
|
57
59
|
|
|
58
60
|
## TDD Exception Rule
|
|
59
61
|
|
|
@@ -78,11 +80,14 @@
|
|
|
78
80
|
|
|
79
81
|
1. `red_failed`: 已观察到预期失败
|
|
80
82
|
2. `red_reason_verified`: 红灯原因与目标行为缺失一致
|
|
81
|
-
3. `
|
|
82
|
-
4. `
|
|
83
|
-
5. `
|
|
84
|
-
6. `
|
|
85
|
-
7. `
|
|
83
|
+
3. `red_seam_verified`: 红灯通过公共接口、调用方流程、CLI/API/UI 或真实边界进入系统
|
|
84
|
+
4. `red_behavior_verified`: 测试断言用户或调用方可观察行为,不断言内部实现细节
|
|
85
|
+
5. `mock_boundary_verified`: mock 只在系统边界,内部协作者没有被 mock
|
|
86
|
+
6. `green_passed`: 当前任务实现转绿
|
|
87
|
+
7. `refactor_done` 或 `refactor_not_needed`
|
|
88
|
+
8. `refactor_green`: 重构后相关测试仍绿
|
|
89
|
+
9. `spec_review_pass`
|
|
90
|
+
10. `code_review_pass`
|
|
86
91
|
|
|
87
92
|
任何一门失败,都回到实现,不准直接跨过去。
|
|
88
93
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cc-do
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.6.0
|
|
4
4
|
description: Use when implementing planned tasks, resuming interrupted work, applying a frozen investigation handoff, or landing review feedback after cc-plan or cc-investigate.
|
|
5
5
|
triggers:
|
|
6
6
|
- 开始做 T003
|
|
@@ -36,7 +36,7 @@ entry_gate:
|
|
|
36
36
|
- Select only ready tasks whose dependencies and file ownership are clear.
|
|
37
37
|
- If the current task cannot be restated from canonical artifacts, run a context reset before coding.
|
|
38
38
|
exit_criteria:
|
|
39
|
-
- The current task has red/green evidence, review evidence, and a resumable checkpoint trail.
|
|
39
|
+
- The current task has red/green evidence, public-seam test quality evidence, review evidence, and a resumable checkpoint trail.
|
|
40
40
|
- Execution leaves the next verifier enough runtime truth to judge the task without chat memory.
|
|
41
41
|
- The honest next step is cc-check or an explicit reroute.
|
|
42
42
|
reroutes:
|
|
@@ -134,8 +134,14 @@ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
|
|
|
134
134
|
3. Refactor:只有 Green 之后才能清理命名、重复、结构和坏味道。
|
|
135
135
|
4. Record:每一站都写入 `checkpoint.json`,必要时写入 `events.jsonl`。
|
|
136
136
|
|
|
137
|
+
Red 不是形式上的红,而是公共 seam 上的行为缺失证明。测试必须通过公共接口、调用方流程、CLI/API/UI 路径或其它真实边界进入系统;只验证私有函数、内部调用次数、临时数据结构或 mock 自己控制的内部协作者,不算 TDD 证据。
|
|
138
|
+
|
|
137
139
|
例外只能用于 throwaway prototype、纯生成文件、纯配置改动;例外必须写进 checkpoint 的 `tddException`,包含原因、风险和替代验证命令。测试第一次就绿,说明测试没有证明新行为,必须修测试而不是继续写生产代码。
|
|
138
140
|
|
|
141
|
+
禁止水平切片:不要先写一批测试,再写一批实现。每次只推进一个 tracer bullet:一个可观察行为的 Red -> 让它变绿的最小实现 -> 必要重构 -> 记录证据,然后再进入下一个行为。
|
|
142
|
+
|
|
143
|
+
测试数据也必须诚实。fixture 只提供当前行为需要的最小输入;partial fixture、类型断言、mock payload 或 generated stub 必须写清哪些字段是真实 contract,哪些只是测试填充。不能用 `as`、`any`、双重 cast、缺字段 partial mock 或 test-only method 掩盖 seam 设计问题。
|
|
144
|
+
|
|
139
145
|
## Entry Gate
|
|
140
146
|
|
|
141
147
|
1. 先读 `planning/design.md` 或 `planning/analysis.md`,再读 `planning/tasks.md`、`planning/task-manifest.json`;如果是恢复执行,再补读最近 checkpoint 或已有 `handoff/resume-index.md`。
|
|
@@ -151,11 +157,13 @@ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
|
|
|
151
157
|
3. 没有明确并行资格,不准把多个实现任务同时推进。
|
|
152
158
|
4. 先 `fail-first`:先写失败测试,先看见预期红,再写生产代码。
|
|
153
159
|
5. 如果红灯不是预期失败(语法错、fixture 错、测试没连上),先修测试直到它正确失败。
|
|
154
|
-
6.
|
|
155
|
-
7. Refactor
|
|
156
|
-
8.
|
|
157
|
-
9.
|
|
158
|
-
10.
|
|
160
|
+
6. 如果红灯通过错误 seam 得到,比如私有方法、内部调用次数、mock 内部协作者,先修测试 seam,不准进入 Green。
|
|
161
|
+
7. 按 `Red -> Green -> Refactor` 推进,Green 只允许最小实现。
|
|
162
|
+
8. 如果当前 Red 需要新的 fixture 或 mock,先证明它仍从公共 seam 触发真实行为;fixture 缺字段、类型强转或内部 mock 都要写入 `tdd.testQuality.fixtureRisk` 或先修 seam。
|
|
163
|
+
9. Refactor 后必须重跑相关测试,保持 Green。
|
|
164
|
+
10. 每次推进都写 task runtime:`events.jsonl` + `checkpoint.json`,并记录 `tdd.testQuality` 或 `tddException`。
|
|
165
|
+
11. 任务实现后,先过 `spec review`,再过 `code review`,两道门都过才算任务收口;这里只验证 spec delta,不回写长期 spec。
|
|
166
|
+
12. 当前任务完成后,把可验证证据留给 `cc-check`。
|
|
159
167
|
|
|
160
168
|
## Output
|
|
161
169
|
|
|
@@ -168,7 +176,8 @@ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
|
|
|
168
176
|
## Good Output
|
|
169
177
|
|
|
170
178
|
- 当前 task 一眼可见,执行者不用从聊天记录里猜目标
|
|
171
|
-
- 至少留下一次明确的 Red/Green/Refactor 证据,且 Red
|
|
179
|
+
- 至少留下一次明确的 tracer bullet Red/Green/Refactor 证据,且 Red 是公共 seam 上的预期行为失败
|
|
180
|
+
- 测试 fixture 说明真实 contract 字段和测试填充字段,没有用类型欺骗或内部 mock 制造假绿
|
|
172
181
|
- runtime / checkpoint 足够让下一位接手者无损恢复
|
|
173
182
|
- reviewer 能顺着 review 记录和验证命令复盘这次实现
|
|
174
183
|
|
|
@@ -194,11 +203,13 @@ NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
|
|
|
194
203
|
3. 没有失败测试,不准写生产代码。
|
|
195
204
|
4. 测试如果第一次就绿,说明你没证明任何东西,先修测试。
|
|
196
205
|
5. 红灯原因必须和目标行为缺失一致;红灯如果只是测试写错,不算 TDD 证据。
|
|
197
|
-
6.
|
|
198
|
-
7.
|
|
199
|
-
8.
|
|
200
|
-
9.
|
|
201
|
-
10.
|
|
206
|
+
6. 红灯必须验证公共接口上的行为;实现细节测试、私有方法测试、内部调用次数断言都要先退回 Red 修正。
|
|
207
|
+
7. Mock 只能放在系统边界;如果必须 mock 内部协作者才能测试,说明 seam 或设计合同有问题。
|
|
208
|
+
8. 先过 `spec review`,再过 `code review`,顺序不能反。
|
|
209
|
+
9. 不在 `cc-do` 里改 capability spec 正文;这里只产出实现证据和 spec 对齐证据。
|
|
210
|
+
10. 失败和阻塞都要留下恢复证据。
|
|
211
|
+
11. 给 subagent 的输入必须包含:当前进度、当前任务全文、依赖状态、必读文件、验收标准、可信命令。
|
|
212
|
+
12. 三次失败修补后必须先质疑调查合同或设计合同,而不是继续堆补丁。
|
|
202
213
|
|
|
203
214
|
## Exit Criteria
|
|
204
215
|
|
|
@@ -37,11 +37,14 @@
|
|
|
37
37
|
1. `context_ready`
|
|
38
38
|
2. `red_failed`
|
|
39
39
|
3. `red_reason_verified`
|
|
40
|
-
4. `
|
|
41
|
-
5. `
|
|
42
|
-
6. `
|
|
43
|
-
7. `
|
|
44
|
-
8. `
|
|
40
|
+
4. `red_seam_verified`
|
|
41
|
+
5. `red_behavior_verified`
|
|
42
|
+
6. `mock_boundary_verified`
|
|
43
|
+
7. `green_passed`
|
|
44
|
+
8. `refactor_done` 或 `refactor_not_needed`
|
|
45
|
+
9. `refactor_green`
|
|
46
|
+
10. `spec_review_pass`
|
|
47
|
+
11. `code_review_pass`
|
|
45
48
|
|
|
46
49
|
如果 `events.jsonl` 没开启,至少仍要有最新 `checkpoint.json` 和 manifest review verdict。
|
|
47
50
|
|
|
@@ -52,9 +55,17 @@
|
|
|
52
55
|
- `red.command`
|
|
53
56
|
- `red.exitStatus`
|
|
54
57
|
- `red.expectedFailure`
|
|
58
|
+
- `red.testSeam`
|
|
59
|
+
- `red.behaviorAsserted`
|
|
60
|
+
- `red.allowedMocks`
|
|
61
|
+
- `red.implementationDetailRisk`
|
|
55
62
|
- `green.command`
|
|
56
63
|
- `green.exitStatus`
|
|
57
64
|
- `refactor.status`
|
|
65
|
+
- `testQuality.usesPublicInterface`
|
|
66
|
+
- `testQuality.describesBehavior`
|
|
67
|
+
- `testQuality.survivesInternalRefactor`
|
|
68
|
+
- `testQuality.mocksOnlySystemBoundaries`
|
|
58
69
|
- `review.spec.status`
|
|
59
70
|
- `review.code.status`
|
|
60
71
|
|
|
@@ -82,12 +82,25 @@ if [[ -f "$events_file" ]]; then
|
|
|
82
82
|
echo "-1"
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
assert_before() {
|
|
86
|
+
local before="$1"
|
|
87
|
+
local after="$2"
|
|
88
|
+
local before_idx after_idx
|
|
89
|
+
before_idx="$(first_index "$before")"
|
|
90
|
+
after_idx="$(first_index "$after")"
|
|
91
|
+
if [[ "$before_idx" != "-1" && "$after_idx" != "-1" && "$before_idx" -ge "$after_idx" ]]; then
|
|
92
|
+
echo "Task $TASK_ID gate order is invalid: $before must precede $after" >&2
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
assert_before "red_failed" "red_reason_verified"
|
|
98
|
+
assert_before "red_reason_verified" "red_seam_verified"
|
|
99
|
+
assert_before "red_seam_verified" "red_behavior_verified"
|
|
100
|
+
assert_before "red_behavior_verified" "mock_boundary_verified"
|
|
101
|
+
assert_before "mock_boundary_verified" "green_passed"
|
|
102
|
+
assert_before "red_failed" "green_passed"
|
|
103
|
+
assert_before "green_passed" "refactor_green"
|
|
91
104
|
fi
|
|
92
105
|
fi
|
|
93
106
|
|