cc-devflow 4.5.7 → 4.5.9

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 (105) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +33 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +18 -4
  3. package/.claude/skills/cc-act/SKILL.md +76 -7
  4. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +30 -0
  5. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +29 -0
  6. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +103 -0
  7. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +60 -4
  8. package/.claude/skills/cc-act/references/closure-contract.md +7 -0
  9. package/.claude/skills/cc-act/references/git-commit-guidelines.md +342 -37
  10. package/.claude/skills/cc-act/scripts/cc-act-common.sh +29 -1
  11. package/.claude/skills/cc-act/scripts/detect-ship-target.sh +27 -0
  12. package/.claude/skills/cc-act/scripts/ensure-ship-branch.sh +93 -0
  13. package/.claude/skills/cc-act/scripts/generate-status-report.sh +6 -0
  14. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +170 -0
  15. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +15 -1
  16. package/.claude/skills/cc-dev/CHANGELOG.md +5 -0
  17. package/.claude/skills/cc-dev/PLAYBOOK.md +63 -0
  18. package/.claude/skills/cc-dev/SKILL.md +168 -0
  19. package/.claude/skills/cc-do/CHANGELOG.md +17 -0
  20. package/.claude/skills/cc-do/SKILL.md +41 -13
  21. package/.claude/skills/cc-do/scripts/build-task-context.sh +9 -5
  22. package/.claude/skills/cc-do/scripts/mark-task-complete.sh +0 -6
  23. package/.claude/skills/cc-investigate/CHANGELOG.md +17 -0
  24. package/.claude/skills/cc-investigate/PLAYBOOK.md +15 -0
  25. package/.claude/skills/cc-investigate/SKILL.md +46 -1
  26. package/.claude/skills/cc-investigate/assets/ANALYSIS_TEMPLATE.md +47 -0
  27. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +21 -2
  28. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +28 -58
  29. package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -0
  30. package/.claude/skills/cc-next/CHANGELOG.md +11 -0
  31. package/.claude/skills/cc-next/PLAYBOOK.md +74 -0
  32. package/.claude/skills/cc-next/SKILL.md +196 -0
  33. package/.claude/skills/cc-plan/CHANGELOG.md +25 -0
  34. package/.claude/skills/cc-plan/PLAYBOOK.md +25 -20
  35. package/.claude/skills/cc-plan/SKILL.md +116 -13
  36. package/.claude/skills/cc-plan/assets/DESIGN_TEMPLATE.md +67 -0
  37. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +85 -0
  38. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +57 -182
  39. package/.claude/skills/cc-plan/assets/TINY_DESIGN_TEMPLATE.md +46 -0
  40. package/.claude/skills/cc-plan/references/planning-contract.md +51 -26
  41. package/.claude/skills/cc-pr-land/CHANGELOG.md +5 -0
  42. package/.claude/skills/cc-pr-land/PLAYBOOK.md +45 -0
  43. package/.claude/skills/cc-pr-land/SKILL.md +157 -0
  44. package/.claude/skills/cc-pr-review/CHANGELOG.md +5 -0
  45. package/.claude/skills/cc-pr-review/PLAYBOOK.md +46 -0
  46. package/.claude/skills/cc-pr-review/SKILL.md +142 -0
  47. package/.claude/skills/cc-review/CHANGELOG.md +21 -0
  48. package/.claude/skills/cc-review/PLAYBOOK.md +64 -10
  49. package/.claude/skills/cc-review/SKILL.md +185 -18
  50. package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +4 -0
  51. package/.claude/skills/cc-review/references/implementation-review-branch.md +37 -0
  52. package/.claude/skills/cc-review/references/plan-review-branch.md +36 -1
  53. package/.claude/skills/cc-review/references/review-methods.md +98 -3
  54. package/.claude/skills/cc-review/scripts/collect-review-context.sh +80 -0
  55. package/.claude/skills/cc-roadmap/CHANGELOG.md +6 -0
  56. package/.claude/skills/cc-roadmap/PLAYBOOK.md +30 -0
  57. package/.claude/skills/cc-roadmap/SKILL.md +45 -8
  58. package/.claude/skills/cc-roadmap/assets/BACKLOG_TEMPLATE.md +8 -0
  59. package/.claude/skills/cc-roadmap/assets/ROADMAP_TEMPLATE.md +22 -0
  60. package/.claude/skills/cc-roadmap/assets/TRACKING_TEMPLATE.json +32 -1
  61. package/.claude/skills/cc-roadmap/references/roadmap-dialogue.md +14 -14
  62. package/.claude/skills/cc-simplify/CHANGELOG.md +6 -0
  63. package/.claude/skills/cc-simplify/SKILL.md +19 -8
  64. package/CHANGELOG.md +20 -1
  65. package/README.md +60 -9
  66. package/README.zh-CN.md +60 -9
  67. package/config/distributable-skills.json +8 -0
  68. package/docs/assets/cc-devflow-pr-harness-en.svg +153 -0
  69. package/docs/assets/cc-devflow-pr-harness-zh.svg +152 -0
  70. package/docs/assets/wechat-group-qr.jpg +0 -0
  71. package/docs/examples/example-bindings.json +11 -7
  72. package/docs/examples/full-design-blocked/BACKLOG.md +1 -1
  73. package/docs/examples/full-design-blocked/README.md +1 -1
  74. package/docs/examples/full-design-blocked/ROADMAP.md +1 -1
  75. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +1 -1
  76. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +102 -82
  77. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/tasks.md +45 -1
  78. package/docs/examples/full-design-blocked/roadmap.json +1 -1
  79. package/docs/examples/local-handoff/BACKLOG.md +1 -1
  80. package/docs/examples/local-handoff/README.md +1 -1
  81. package/docs/examples/local-handoff/ROADMAP.md +1 -1
  82. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +1 -1
  83. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +70 -61
  84. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/tasks.md +35 -1
  85. package/docs/examples/local-handoff/roadmap.json +1 -1
  86. package/docs/examples/pdca-loop/BACKLOG.md +1 -1
  87. package/docs/examples/pdca-loop/README.md +1 -1
  88. package/docs/examples/pdca-loop/ROADMAP.md +1 -1
  89. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +64 -0
  90. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +1 -1
  91. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +71 -81
  92. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/tasks.md +35 -1
  93. package/docs/examples/pdca-loop/roadmap.json +1 -1
  94. package/docs/examples/scripts/check-example-bindings.sh +24 -2
  95. package/docs/get-shit-done-strategy-audit.md +4 -4
  96. package/docs/guides/artifact-contract.md +44 -0
  97. package/docs/guides/getting-started.md +1 -1
  98. package/docs/guides/getting-started.zh-CN.md +1 -1
  99. package/docs/guides/project-postmortem.md +78 -0
  100. package/lib/skill-runtime/__tests__/planner.tdd.test.js +2 -2
  101. package/lib/skill-runtime/__tests__/schemas.test.js +33 -2
  102. package/lib/skill-runtime/planner.js +1 -2
  103. package/lib/skill-runtime/query.js +1 -1
  104. package/lib/skill-runtime/schemas.js +5 -3
  105. package/package.json +6 -1
@@ -2,15 +2,80 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- `cc-act` 在执行 `create-pr` / `update-pr` 时,必须让提交历史满足 3 个目标:
5
+ `cc-act` 在执行 `create-pr` / `update-pr` 时,必须让 Git 历史成为可 review、可 bisect、可 revert、可交接的工程记录,而不是一句含糊的摘要。
6
6
 
7
- 1. 提交信息清楚、可追溯
8
- 2. 提交边界干净、不要把不同类型变更揉成一团
9
- 3. 分支历史尽量线性,便于 review 和回滚
7
+ 提交记录要同时满足 5 个目标:
10
8
 
11
- ## Commit Message Format
9
+ 1. `git log --oneline` 能看懂变更类型和范围。
10
+ 2. `git show` 能解释问题、取舍、验证和风险。
11
+ 3. 每个 commit 边界足够小,后续可以独立 review、cherry-pick 或 revert。
12
+ 4. PR body、release note、handoff 和 commit history 讲同一套事实。
13
+ 5. 后续维护者不需要聊天记录,也能知道为什么这么改。
14
+
15
+ ## Research Baseline
16
+
17
+ 本规范吸收当前公开实践,但落成 `cc-devflow` 自己的提交合同:
18
+
19
+ - Git 官方 `git commit` 文档建议首行少于 50 字符,空一行后写更完整描述;Git 会把首个空行前内容当成 title。
20
+ - Git 官方 `SubmittingPatches` 强调提交说明和代码同样重要,要解释 future maintainer 需要知道的 `why`,并使用祈使句。
21
+ - Conventional Commits 1.0.0 给出机器可读标题、body、footer 结构,`feat` / `fix` / `BREAKING CHANGE` 可驱动 changelog 和 semver。
22
+ - Git trailer 规范支持结构化 footer,例如 `Reviewed-by`、`Signed-off-by`、`Refs`。
23
+ - GitHub 支持在 commit message 或 PR 描述里用 closing keywords 关联和关闭 issue。
24
+ - 近期提交信息研究提示:只有 Conventional Commit 标题还不够,很多提交仍然信息量不足;所以本规范要求正文模板,而不是只要求 `type(scope): subject`。
12
25
 
13
- 默认使用 Conventional Commits:
26
+ ## Commit Record Contract
27
+
28
+ 默认使用 Conventional Commits 标题,但非平凡提交必须带正文。
29
+
30
+ `type(scope)` 保持 ASCII 和机器可读;`subject`、正文标题、正文内容默认使用项目 `Output language`。中文项目输出中文 commit 文本,不能照抄英文模板。
31
+
32
+ ```text
33
+ <type>(<scope>): <中文 subject>
34
+
35
+ 问题:
36
+ - <哪里坏了、缺了、风险在哪里、读者为什么要关心>
37
+
38
+ 变更:
39
+ - <持久实现、模板、脚本、产物或契约变化>
40
+ - <关键边界、数据形状、工作流决策>
41
+
42
+ 原因:
43
+ - <为什么当前方案是正确取舍>
44
+ - <为什么没有选择更窄、更宽或其它替代方案>
45
+
46
+ 验证:
47
+ - <命令、产物、review gate,或明确 not-run 原因>
48
+
49
+ 风险:
50
+ - <影响范围、迁移问题、回滚路径,或“低:仅文档/模板”>
51
+
52
+ 关联:
53
+ - <REQ/FIX/RM/issue/PR/spec/review artifact,适用时填写>
54
+
55
+ <footer trailers when applicable>
56
+ ```
57
+
58
+ 正文不是复述 diff。正文解释 diff 看不出来的上下文:为什么改、边界在哪里、怎么证明、哪里可能坏。
59
+
60
+ ## When Body Is Required
61
+
62
+ 只允许非常小的机械变更使用单行 commit。满足任一条件时必须写 body:
63
+
64
+ 1. 修改超过 1 个文件。
65
+ 2. 改变用户可见行为、公共 API、CLI、schema、prompt、skill contract、验证脚本或发布流程。
66
+ 3. 修 bug、回归、flaky、性能、安全、权限、数据一致性或 trust boundary。
67
+ 4. 引入或改变测试策略、mock 边界、fixture、golden artifact。
68
+ 5. 触碰 roadmap / planning / task manifest / report-card / handoff 等 durable artifact。
69
+ 6. 有兼容性、迁移、回滚、release note 或 follow-up 风险。
70
+
71
+ 单行 commit 仅适合:
72
+
73
+ - typo / spelling
74
+ - 纯格式化且无行为变化
75
+ - 单文件注释澄清
76
+ - 生成物版本号同步,且 changelog 或 PR body 已解释原因
77
+
78
+ ## Commit Message Format
14
79
 
15
80
  ```text
16
81
  <type>(<scope>): <subject>
@@ -20,64 +85,304 @@
20
85
  <footer>
21
86
  ```
22
87
 
23
- 如果当前仓库已有更严格的提交规范,以仓库规范为准;否则沿用这里的格式。
88
+ 规则:
89
+
90
+ 1. `type` 必须表达变更性质。
91
+ 2. `scope` 必须是 repo 里真实模块、skill、capability、script、doc area 或 package 名;不要写 `misc` / `stuff`。
92
+ 3. `subject` 使用项目输出语言;中文项目写中文短句,`type(scope)` 仍保持 Conventional Commits ASCII 前缀。
93
+ 4. `subject` 目标 50 字符以内;超过时先缩 scope 或拆 commit。
94
+ 5. `body` 使用上面的中文结构化标题;非平凡提交不要只写一段泛泛说明。
95
+ 6. `footer` 使用 Git trailer 风格:`Token: value`,每个 trailer 独立一行。
96
+ 7. 如果当前仓库已有更严格规范,以仓库规范为准;但不能低于本文件的信息量要求。
24
97
 
25
98
  ## Allowed Types
26
99
 
27
- - `feat`: 新功能
28
- - `fix`: 修复缺陷
29
- - `docs`: 文档更新
100
+ - `feat`: 新功能或新增可观察能力
101
+ - `fix`: 修复缺陷、回归或错误行为
102
+ - `docs`: 文档、指南、示例文字
30
103
  - `style`: 纯格式调整,不改行为
31
104
  - `refactor`: 重构,不改外部行为
32
105
  - `perf`: 性能优化
33
- - `test`: 测试变更
34
- - `chore`: 构建、脚本、依赖、工具链
106
+ - `test`: 测试、fixture、golden artifact、验证 seam
107
+ - `build`: 构建系统、打包、发布配置
108
+ - `ci`: CI / workflow / automation
109
+ - `chore`: 依赖、脚本、仓库维护、生成物同步
110
+ - `revert`: 回滚既有 commit
111
+
112
+ ## Body Templates By Type
113
+
114
+ ### `feat`
115
+
116
+ ```text
117
+ 问题:
118
+ - <谁以前无法完成什么>
119
+
120
+ 变更:
121
+ - <新增能力>
122
+ - <公共接口、产物、命令或用户可见流程>
123
+
124
+ 原因:
125
+ - <为什么这个范围是当前正确切片>
126
+ - <哪些内容明确不在本次范围内>
127
+
128
+ 验证:
129
+ - <命令或人工证明>
130
+
131
+ 风险:
132
+ - <兼容性、发布、迁移、回滚>
133
+
134
+ 关联:
135
+ - <REQ/RM/spec/issue>
136
+ ```
137
+
138
+ ### `fix`
139
+
140
+ ```text
141
+ 问题:
142
+ - <报告的现象和影响>
143
+
144
+ 根因:
145
+ - <first bad state 或被破坏的契约>
146
+
147
+ 变更:
148
+ - <最小修复>
149
+ - <为什么这不是只补症状>
150
+
151
+ 验证:
152
+ - <可用时写 failing-before / passing-after 证据>
153
+ - <回归命令>
154
+
155
+ 风险:
156
+ - <影响范围和回滚路径>
157
+
158
+ 关联:
159
+ - <FIX/issue/analysis/report-card>
160
+ ```
161
+
162
+ ### `refactor`
163
+
164
+ ```text
165
+ 问题:
166
+ - <坏味道、耦合、重复或维护风险>
167
+
168
+ 不变量:
169
+ - <必须保持不变的行为>
170
+
171
+ 变更:
172
+ - <移动、重命名、抽取或收敛的结构>
173
+
174
+ 原因:
175
+ - <为什么这会简化系统>
176
+ - <为什么兼容性被保留或为何要改变>
177
+
178
+ 验证:
179
+ - <测试、类型检查、diff check>
180
+
181
+ 风险:
182
+ - <触碰的调用方和回滚路径>
183
+ ```
184
+
185
+ ### `test`
35
186
 
36
- ## Message Rules
187
+ ```text
188
+ 问题:
189
+ - <以前没有覆盖的行为或回归>
190
+
191
+ 变更:
192
+ - <新增的测试 seam、fixture 或断言>
193
+
194
+ 原因:
195
+ - <为什么这个 seam 能触达真实触发链>
196
+ - <为什么更浅的覆盖会制造假安全>
197
+
198
+ 验证:
199
+ - <测试命令和预期信号>
200
+
201
+ 风险:
202
+ - <flaky 风险、fixture 维护成本、运行耗时>
203
+ ```
204
+
205
+ ### `docs`
206
+
207
+ ```text
208
+ 问题:
209
+ - <读者困惑、契约过期、缺 runbook 或文档漂移>
210
+
211
+ 变更:
212
+ - <更新的章节或示例>
213
+
214
+ 原因:
215
+ - <使用的真相源>
216
+ - <未来读者不再需要追问什么>
217
+
218
+ 验证:
219
+ - <链接检查、示例检查、渲染检查,或 not-run 原因>
220
+
221
+ 风险:
222
+ - <低,或文档继续漂移的风险>
223
+ ```
224
+
225
+ ### `build` / `ci` / `chore`
226
+
227
+ ```text
228
+ 问题:
229
+ - <工具链、依赖、发布或自动化缺口>
230
+
231
+ 变更:
232
+ - <配置、脚本、package、lockfile、生成物>
233
+
234
+ 原因:
235
+ - <为什么现在需要这个工具链变更>
236
+
237
+ 验证:
238
+ - <真实工具入口或最接近的 smoke>
239
+
240
+ 风险:
241
+ - <环境、版本、发布、回滚>
242
+ ```
243
+
244
+ ### `revert`
245
+
246
+ ```text
247
+ 问题:
248
+ - <为什么被回滚的 commit 不安全或错误>
249
+
250
+ 变更:
251
+ - 回滚 <sha> (<subject>)。
252
+
253
+ 原因:
254
+ - <为什么现在回滚优于向前修复>
37
255
 
38
- 1. `subject` 保持简短,优先控制在 50 个字符内。
39
- 2. `subject` 使用祈使句,首字母小写,末尾不加句号。
40
- 3. `body` 只解释 `why` 和关键 `how`,不要把 diff 逐行复述。
41
- 4. `footer` 只在需要时使用,例如 `BREAKING CHANGE:` 或 `Closes #123`。
42
- 5. PR 标题应与最终交付的核心 commit 语义一致,不要标题和提交说两套话。
256
+ 验证:
257
+ - <回滚后的命令或证据>
258
+
259
+ 风险:
260
+ - <丢失的行为、follow-up、回滚本次回滚的路径>
261
+ ```
43
262
 
44
263
  ## Commit Boundary Rules
45
264
 
46
- `cc-act` 生成 commit 时,优先消除“一个 commit 混很多事”这种坏味道。
265
+ `cc-act` 生成 commit 前必须先写 commit plan。不要先 `git add .` 再想 message。
266
+
267
+ Commit plan 模板:
268
+
269
+ ```text
270
+ Commit 1:
271
+ - 类型/scope:
272
+ - 文件:
273
+ - 分组原因:
274
+ - 验证:
275
+ - Commit 文本草稿:
276
+
277
+ Commit 2:
278
+ - 类型/scope:
279
+ - 文件:
280
+ - 分组原因:
281
+ - 验证:
282
+ - Commit 文本草稿:
283
+ ```
284
+
285
+ 拆分规则:
47
286
 
48
287
  1. 功能、测试、文档、配置属于不同类型时,拆成多个 commit。
49
288
  2. 多个模块彼此独立时,按模块拆分 commit。
50
- 3. bug 与补防御性代码不是同一件事,能拆就拆。
51
- 4. 只有当改动天然不可分割时,才允许合并到同一个 commit。
52
- 5. 禁止为了省事直接 `git add .` 把无关变更一起提交。
289
+ 3. 数据模型、服务层、API、UI 横跨多层时,优先按层拆分,除非某层单独无法通过验证。
290
+ 4. bug 与补防御性 guard / follow-up 不是同一件事,能拆就拆。
291
+ 5. 生成物和源文件一起提交,必须在 body 里说明生成关系和验证命令。
292
+ 6. 一个 commit 如果预期触碰超过 8 个文件,先问自己是否混了多个语义;不能拆时在 body 的 `Risk` 里说明原因。
293
+ 7. 每个 commit 尽量独立可验证。不能独立跑全量测试时,至少给出最贴近该 commit 的验证。
53
294
 
54
295
  推荐拆分顺序:
55
296
 
56
- - 核心实现
57
- - 测试
58
- - 文档
59
- - 配置或工具链
297
+ 1. 数据 / schema / contract
298
+ 2. 核心实现
299
+ 3. 测试
300
+ 4. 文档 / examples
301
+ 5. 构建 / 发布 / 生成物
60
302
 
61
303
  ## History Rules
62
304
 
63
- 1. 推送前优先同步 base branch,并尽量使用 `git pull --rebase` 保持线性历史。
64
- 2. 已有打开的 PR 时,更新现有 PR,不重复创建第二个。
305
+ 1. 推送前优先同步 base branch,并尽量用 `git pull --rebase` 或 `git rebase <base>` 保持线性历史。
306
+ 2. 已有打开 PR / MR 时,更新现有 PR / MR,不重复创建第二个。
65
307
  3. rebase 之后如果必须强推,只允许 `git push --force-with-lease`,不要裸 `--force`。
308
+ 4. 不改写公共分支历史。
309
+ 5. 不把 WIP、checkpoint、debug probe、临时日志留在最终历史里;必要时 squash/fixup 到语义 commit。
310
+ 6. 使用 `fixup!` / `squash!` 时,最终 ship 前要 autosquash 成干净历史,除非团队明确接受。
311
+
312
+ ## Footer Trailers
313
+
314
+ Footer 用于机器和长期审计,不用于塞正文。
315
+
316
+ 常用 trailers:
66
317
 
67
- ## Examples
318
+ - `BREAKING CHANGE: <what breaks and migration path>`
319
+ - `Closes: #123`
320
+ - `Refs: #123`
321
+ - `Reviewed-by: <name>`
322
+ - `Co-authored-by: <name> <email>`
323
+ - `Signed-off-by: <name> <email>`,仅在项目要求 DCO / signoff 时使用
324
+ - `Change-Id: <id>`,仅在 Gerrit 或项目要求时使用
325
+
326
+ GitHub 自动关闭 issue 时,每个 issue 都要带 closing keyword,例如:
68
327
 
69
328
  ```text
70
- feat(user): add wechat login flow
71
- fix(auth): handle expired token refresh
72
- test(payment): cover callback retry path
73
- docs(readme): document local development setup
74
- refactor(api): simplify request error mapping
329
+ Closes: #10
330
+ Resolves: #123
331
+ Fixes: owner/repo#100
332
+ ```
333
+
334
+ 只想关联不想关闭时,用 `Refs:`,不要用 closing keyword。
335
+
336
+ ## Bad Messages
337
+
338
+ 禁止这些标题:
339
+
340
+ - `fix bug`
341
+ - `update files`
342
+ - `misc changes`
343
+ - `wip`
344
+ - `improve stuff`
345
+ - `final changes`
346
+ - `address feedback`
347
+ - `cleanup`
348
+ - `changes from review`
349
+
350
+ 可以改成:
351
+
352
+ ```text
353
+ fix(auth): 拒绝过期 refresh token
354
+
355
+ 问题:
356
+ - 后台续期流程会接受已经过期的 refresh token。
357
+
358
+ 根因:
359
+ - 续期路径信任缓存的过期标记,而不是 token payload 里的真实过期时间。
360
+
361
+ 变更:
362
+ - 发放替换 token 前先解析 payload 过期时间。
363
+ - 缓存只保留为优化手段,不再作为信任边界。
364
+
365
+ 验证:
366
+ - npm test -- auth-token-refresh.test.ts
367
+
368
+ 风险:
369
+ - 低:只影响 auth 续期路径;回滚会恢复旧缓存检查。
370
+
371
+ 关联:
372
+ - FIX-042
75
373
  ```
76
374
 
77
375
  ## CC-Act Enforcement
78
376
 
79
377
  `cc-act` 在 `create-pr` / `update-pr` 模式下至少要检查:
80
378
 
81
- 1. commit message 是否符合本规范或仓库已有规范
82
- 2. 是否把明显不同类型的变更拆成了独立 commit
83
- 3. push PR 描述是否与最终提交历史表达同一套事实
379
+ 1. 是否先列出 commit plan,再 staging。
380
+ 2. staged files 是否只属于当前 commit bucket。
381
+ 3. commit title 是否符合 Conventional Commits 或仓库更严格规范。
382
+ 4. 非平凡 commit 是否有 `问题`、`变更`、`原因`、`验证`、`风险`。
383
+ 5. `fix` commit 是否写了 `根因`,且不是只描述 symptom。
384
+ 6. `验证` 是否是实际命令 / artifact / explicit skip reason,而不是 “tested locally”。
385
+ 7. footer 是否使用 trailer 风格,issue closing keyword 是否符合目标平台语义。
386
+ 8. push、PR body、release note、handoff 是否与最终 commit history 表达同一套事实。
387
+
388
+ 如果无法满足这些条件,停在 `local-handoff` 或 reroute,不要制造粗糙历史。
@@ -34,6 +34,10 @@ req_act_manifest_path() {
34
34
  printf '%s/task-manifest.json\n' "$(req_act_planning_dir "$1")"
35
35
  }
36
36
 
37
+ req_act_change_meta_path() {
38
+ printf '%s/change-meta.json\n' "$(req_act_change_dir "$1")"
39
+ }
40
+
37
41
  req_act_tasks_path() {
38
42
  printf '%s/tasks.md\n' "$(req_act_planning_dir "$1")"
39
43
  }
@@ -243,11 +247,22 @@ req_act_collect_touched_files() {
243
247
  req_act_collect_spec_files() {
244
248
  local manifest="$1"
245
249
  local out_file="$2"
250
+ local change_dir=""
251
+ local change_meta=""
246
252
 
247
253
  : > "$out_file"
248
254
 
249
255
  if [[ -f "$manifest" ]]; then
250
- jq -r '(.spec.specFiles // [])[]?' "$manifest" 2>/dev/null | sed '/^$/d' > "$out_file" || true
256
+ change_dir="$(req_act_change_dir "$(dirname "$manifest")")"
257
+ change_meta="$(req_act_change_meta_path "$change_dir")"
258
+ fi
259
+
260
+ if [[ -n "$change_meta" && -f "$change_meta" ]]; then
261
+ jq -r '(.spec.specFiles // [])[]?' "$change_meta" 2>/dev/null | sed '/^$/d' > "$out_file" || true
262
+ fi
263
+
264
+ if [[ -f "$manifest" ]]; then
265
+ jq -r '(.spec.specFiles // [])[]?' "$manifest" 2>/dev/null | sed '/^$/d' >> "$out_file" || true
251
266
  fi
252
267
 
253
268
  req_act_dedup_file "$out_file"
@@ -262,8 +277,21 @@ req_act_roadmap_sync_summary() {
262
277
  local no_op_reason=""
263
278
  local updated_files=""
264
279
  local existing_files=""
280
+ local change_dir=""
281
+ local change_meta=""
265
282
 
266
283
  if [[ -f "$manifest" ]]; then
284
+ change_dir="$(req_act_change_dir "$(dirname "$manifest")")"
285
+ change_meta="$(req_act_change_meta_path "$change_dir")"
286
+ fi
287
+
288
+ if [[ -n "$change_meta" && -f "$change_meta" ]]; then
289
+ item_id="$(jq -r '.sourceRoadmap.itemId // empty' "$change_meta" 2>/dev/null || true)"
290
+ sync_status="$(jq -r '.roadmapSync.status // .sourceRoadmap.syncStatus // "not recorded"' "$change_meta" 2>/dev/null || true)"
291
+ sync_command="$(jq -r '.roadmapSync.command // .sourceRoadmap.syncCommand // empty' "$change_meta" 2>/dev/null || true)"
292
+ no_op_reason="$(jq -r '.roadmapSync.noOpReason // .sourceRoadmap.noOpReason // empty' "$change_meta" 2>/dev/null || true)"
293
+ updated_files="$(jq -r '(.roadmapSync.updatedFiles // .sourceRoadmap.updatedFiles // []) | join(", ")' "$change_meta" 2>/dev/null || true)"
294
+ elif [[ -f "$manifest" ]]; then
267
295
  item_id="$(jq -r '.sourceRoadmap.itemId // empty' "$manifest" 2>/dev/null || true)"
268
296
  sync_status="$(jq -r '.sourceRoadmap.syncStatus // .roadmapSync.status // "not recorded"' "$manifest" 2>/dev/null || true)"
269
297
  sync_command="$(jq -r '.sourceRoadmap.syncCommand // .roadmapSync.command // empty' "$manifest" 2>/dev/null || true)"
@@ -7,11 +7,24 @@ set -euo pipefail
7
7
  # ------------------------------------------------------------
8
8
 
9
9
  current_branch="$(git branch --show-current 2>/dev/null || true)"
10
+ head_sha="$(git rev-parse --short HEAD 2>/dev/null || true)"
11
+ inside_work_tree="$(git rev-parse --is-inside-work-tree 2>/dev/null || true)"
10
12
  remote_url="$(git remote get-url origin 2>/dev/null || true)"
11
13
  platform="unknown"
12
14
  base_branch=""
13
15
  pr_status="none"
14
16
  pr_url=""
17
+ branch_state="unknown"
18
+ branch_rescue="none"
19
+ rescue_action=""
20
+
21
+ if [[ -n "$current_branch" ]]; then
22
+ branch_state="branch"
23
+ elif [[ "$inside_work_tree" == "true" && -n "$head_sha" ]]; then
24
+ branch_state="detached"
25
+ else
26
+ branch_state="none"
27
+ fi
15
28
 
16
29
  if [[ "$remote_url" == *github.com* ]]; then
17
30
  platform="github"
@@ -71,12 +84,26 @@ elif [[ -n "$remote_url" ]]; then
71
84
  decision_hint="create-pr"
72
85
  fi
73
86
 
87
+ if [[ "$branch_state" == "detached" ]]; then
88
+ if [[ -n "$remote_url" ]]; then
89
+ branch_rescue="create-branch-before-pr"
90
+ rescue_action="Create a named branch at HEAD before commit/push/PR; do not stop only because CURRENT_BRANCH is empty."
91
+ else
92
+ branch_rescue="create-local-branch-or-handoff"
93
+ rescue_action="Create a named local branch at HEAD before local closeout, or write local-handoff if no branch should be created."
94
+ fi
95
+ fi
96
+
74
97
  cat <<EOF
75
98
  CURRENT_BRANCH=$current_branch
99
+ BRANCH_STATE=$branch_state
100
+ HEAD_SHA=$head_sha
76
101
  BASE_BRANCH=$base_branch
77
102
  PLATFORM=$platform
78
103
  REMOTE_URL=$remote_url
79
104
  PR_STATUS=$pr_status
80
105
  PR_URL=$pr_url
81
106
  DECISION_HINT=$decision_hint
107
+ BRANCH_RESCUE=$branch_rescue
108
+ RESCUE_ACTION=$rescue_action
82
109
  EOF
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ # ------------------------------------------------------------
6
+ # cc-act: detached HEAD 时锚定可推送分支
7
+ # ------------------------------------------------------------
8
+
9
+ usage() {
10
+ cat <<'EOF'
11
+ Usage: ensure-ship-branch.sh [--dir path/to/change] [--prefix codex]
12
+ EOF
13
+ }
14
+
15
+ REQ_DIR=""
16
+ BRANCH_PREFIX="${CC_ACT_BRANCH_PREFIX:-codex}"
17
+
18
+ while [[ $# -gt 0 ]]; do
19
+ case "$1" in
20
+ --dir) REQ_DIR="$2"; shift 2 ;;
21
+ --prefix) BRANCH_PREFIX="$2"; shift 2 ;;
22
+ -h|--help) usage; exit 0 ;;
23
+ *) echo "Unknown arg: $1" >&2; usage; exit 1 ;;
24
+ esac
25
+ done
26
+
27
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
28
+ source "$script_dir/cc-act-common.sh"
29
+
30
+ current_branch="$(git branch --show-current 2>/dev/null || true)"
31
+ head_sha="$(git rev-parse --short HEAD 2>/dev/null || true)"
32
+
33
+ if [[ -n "$current_branch" ]]; then
34
+ cat <<EOF
35
+ BRANCH_ACTION=already-on-branch
36
+ CURRENT_BRANCH=$current_branch
37
+ HEAD_SHA=$head_sha
38
+ EOF
39
+ exit 0
40
+ fi
41
+
42
+ if [[ -z "$head_sha" ]]; then
43
+ echo "ShipPreflightError: cannot create a ship branch outside a git work tree" >&2
44
+ exit 1
45
+ fi
46
+
47
+ change_slug=""
48
+ if [[ -n "$REQ_DIR" && -d "$REQ_DIR" ]]; then
49
+ change_slug="$(basename "$(req_act_change_dir "$REQ_DIR")")"
50
+ fi
51
+ if [[ -z "$change_slug" ]]; then
52
+ repo_name="$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)")"
53
+ change_slug="ship-$repo_name"
54
+ fi
55
+
56
+ branch_slug="$(
57
+ printf '%s' "$change_slug" |
58
+ tr '[:upper:]' '[:lower:]' |
59
+ sed -E 's/[^a-z0-9._-]+/-/g; s/^-+//; s/-+$//; s/-+/-/g'
60
+ )"
61
+ if [[ -z "$branch_slug" ]]; then
62
+ branch_slug="ship-$head_sha"
63
+ fi
64
+
65
+ base_branch="${BRANCH_PREFIX%/}/$branch_slug"
66
+ candidate="$base_branch"
67
+
68
+ if git show-ref --verify --quiet "refs/heads/$candidate"; then
69
+ if [[ "$(git rev-parse --short "$candidate" 2>/dev/null || true)" == "$head_sha" ]] &&
70
+ git switch "$candidate" >/dev/null 2>&1; then
71
+ cat <<EOF
72
+ BRANCH_ACTION=switched-existing
73
+ CURRENT_BRANCH=$candidate
74
+ HEAD_SHA=$head_sha
75
+ EOF
76
+ exit 0
77
+ fi
78
+
79
+ candidate="$base_branch-$head_sha"
80
+ suffix=2
81
+ while git show-ref --verify --quiet "refs/heads/$candidate"; do
82
+ candidate="$base_branch-$head_sha-$suffix"
83
+ suffix=$((suffix + 1))
84
+ done
85
+ fi
86
+
87
+ git switch -c "$candidate" >/dev/null
88
+
89
+ cat <<EOF
90
+ BRANCH_ACTION=created
91
+ CURRENT_BRANCH=$candidate
92
+ HEAD_SHA=$head_sha
93
+ EOF
@@ -55,6 +55,9 @@ fi
55
55
 
56
56
  ship_context="$("$script_dir/detect-ship-target.sh" 2>/dev/null || true)"
57
57
  current_branch="$(printf '%s\n' "$ship_context" | awk -F= '/^CURRENT_BRANCH=/{print $2}')"
58
+ branch_state="$(printf '%s\n' "$ship_context" | awk -F= '/^BRANCH_STATE=/{print $2}')"
59
+ branch_rescue="$(printf '%s\n' "$ship_context" | awk -F= '/^BRANCH_RESCUE=/{print $2}')"
60
+ rescue_action="$(printf '%s\n' "$ship_context" | awk -F= '/^RESCUE_ACTION=/{print $2}')"
58
61
  base_branch="$(printf '%s\n' "$ship_context" | awk -F= '/^BASE_BRANCH=/{print $2}')"
59
62
  platform="$(printf '%s\n' "$ship_context" | awk -F= '/^PLATFORM=/{print $2}')"
60
63
  decision_hint="$(printf '%s\n' "$ship_context" | awk -F= '/^DECISION_HINT=/{print $2}')"
@@ -73,9 +76,12 @@ roadmap_sync_summary="$(req_act_roadmap_sync_summary "$manifest")"
73
76
  echo "- Completed tasks: $done_tasks"
74
77
  echo "- Remaining tasks: $remaining_tasks"
75
78
  [[ -n "$current_branch" ]] && echo "- Current branch: $current_branch"
79
+ [[ -n "$branch_state" ]] && echo "- Branch state: $branch_state"
76
80
  [[ -n "$base_branch" ]] && echo "- Base branch: $base_branch"
77
81
  [[ -n "$platform" ]] && echo "- Platform: $platform"
78
82
  [[ -n "$decision_hint" ]] && echo "- Ship mode hint: $decision_hint"
83
+ [[ -n "$branch_rescue" && "$branch_rescue" != "none" ]] && echo "- Branch rescue: $branch_rescue"
84
+ [[ -n "$rescue_action" ]] && echo "- Rescue action: $rescue_action"
79
85
  [[ -n "$pr_status" ]] && echo "- PR status: $pr_status"
80
86
  [[ -n "$pr_url" ]] && echo "- PR url: $pr_url"
81
87
  echo "- Roadmap progress: $roadmap_sync_summary"