@@ -141,7 +141,23 @@ gh run view RUN_ID --log-failed 2>&1 | tail -100
141141
142142### Step 3: 获取未解决的 Review 评论
143143
144- 通过 GraphQL 获取所有 review threads:
144+ Review 反馈分布在三个地方,必须** 都检查** ,不能只看 inline review threads:
145+
146+ 1 . ** Inline review threads** — reviewer 在具体代码行上的评论(GraphQL ` reviewThreads ` )
147+ 2 . ** PR 顶层 issue comments** — review bot 经常把发现的问题汇总成一条整体评论发到 PR 主时间线(` gh pr view --json comments ` )
148+ 3 . ** PR description** — 部分 review bot 会把 summary 写进 PR description 而非 comment
149+
150+ 先获取 PR 作者、body、head SHA,以及最后一次推送对应的 commit 时间:
151+
152+ ``` bash
153+ gh pr view PR_NUMBER --json author,body,headRefOid -q ' {author: .author.login, body: .body, sha: .headRefOid}'
154+ # 用 GitHub 上 head commit 的 committer date 作为"最后一次 push 时间"的近似值
155+ # 不要用本地 git log(rebase/amend 后本地时间和 GitHub 上不一致)
156+ HEAD_SHA=$( gh pr view PR_NUMBER --json headRefOid -q .headRefOid)
157+ LAST_PUSH=$( gh api repos/OWNER/REPO/commits/$HEAD_SHA --jq .commit.committer.date)
158+ ```
159+
160+ ** 3a. Inline review threads** (GraphQL):
145161
146162``` bash
147163gh api graphql -f query=' {
@@ -158,6 +174,7 @@ gh api graphql -f query='{
158174 author { login }
159175 path
160176 line
177+ createdAt
161178 }
162179 }
163180 }
@@ -167,19 +184,45 @@ gh api graphql -f query='{
167184}'
168185```
169186
170- 先获取 PR 作者:` gh pr view PR_NUMBER --json author -q .author.login ` → ` PR_AUTHOR `
171-
172187过滤条件:
173188- ` isResolved == false ` (未解决)
174- - 发起评论(第一条 comment)的 ` author.login ` ** 不是** PR 作者(排除自己的评论,处理所有 reviewer 的反馈,包括 bot 和人类 reviewer)
189+ - 第一条 comment 的 ` author.login ` ** 不是** PR 作者
190+
191+ ** 3b. PR 顶层 issue comments** :
192+
193+ ``` bash
194+ gh pr view PR_NUMBER --json comments -q ' .comments[] | select(.author.login != "PR_AUTHOR") | {id, body, author: .author.login, createdAt}'
195+ ```
196+
197+ 过滤条件:
198+ - ` author.login ` 不是 PR 作者
199+ - ` createdAt ` 在最后一次 push 之后(处理新增反馈,忽略已被旧 commit 处理的历史评论)
200+
201+ ** 3c. PR description** :
202+
203+ 检查 PR body 中是否包含 review summary。判定标准(避免把普通 PR 说明误判为 review):
204+
205+ - ** 强信号** (出现任一即可判定):` ## Review Summary ` 、` ### Issues Found ` 、` ## Review Notes ` 、` Suggested Action `
206+ - ** 弱信号** (需同时出现 ≥2 个才算):` 🟡 ` 、` 🔴 ` 、` nit ` 、` confidence ` 、` severity `
207+
208+ 满足上述任一规则的,把 review summary 段落里的条目当作待处理 review feedback,与 3a/3b 一起进入 Step 4。
209+
210+ ** 去重(重要)** :3a 靠 ` isResolved ` 去重、3b 靠 ` createdAt > LAST_PUSH ` 去重,但 PR description 是静态的,Claude 修完代码 push 后 description 内容并不会变。为避免同一 ` /pr-fixup ` 调用内同一条 3c 条目被反复处理,必须做以下两件事之一:
175211
176- 如果没有 CI 失败(Step 2 已全部通过)且没有未解决的非作者评论 → 输出 "✅ 所有 CI checks 通过,PR review 无阻塞问题" 并结束循环。
212+ - ** 本轮内存记录** :在当前 ` /pr-fixup ` 执行流程中维护一个集合(如条目正文的前 50 字符 hash),处理过的 3c 条目下一轮直接跳过
213+ - ** 镜像到顶层 comment** :处理完 3c 条目后调用 ` gh pr comment ` 写一条 "Addressed (3c): <条目摘要>" 到 PR 主时间线,让后续轮次靠 3b 的 ` LAST_PUSH ` 过滤自动跳过
214+
215+ 推荐第一种(更便宜,不污染 PR 时间线)。
216+
217+ 如果没有 CI 失败(Step 2 已全部通过)且 3a/3b/3c 都没有未处理的反馈 → 输出 "✅ 所有 CI checks 通过,PR review 无阻塞问题" 并结束循环。
177218
178219### Step 4: 分析并处理 Review 评论
179220
180- 对于每个未解决的评论:
221+ 对于每个未解决的评论(来自 3a inline、3b issue comment、3c PR description summary) :
181222
182- 1 . ** 读取完整源文件** :用 Read 工具读取评论所在的 ` path ` 文件
223+ 1 . ** 读取相关源文件** :
224+ - inline 评论:用 Read 工具读取评论所在的 ` path ` 文件
225+ - issue comment / PR description summary:从 body 中解析出涉及的文件路径(通常是 ` src/foo.ts:123 ` 格式),逐个 Read
1832262 . ** 理解评论内容** :仔细阅读 ` body ` 中指出的具体问题
1842273 . ** 结合上下文判断** :评论是否正确?
185228
@@ -200,11 +243,19 @@ gh api graphql -f query='{
200243- 回复评论确认修复:
201244
202245``` bash
246+ # inline review comment(来自 3a)
203247gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_DATABASE_ID/replies \
204248 -f body=" Fixed — <简述修改内容>"
249+
250+ # PR 顶层 issue comment(来自 3b)— 没有 thread,直接在 PR 主时间线新增一条
251+ # 回复链接用完整 URL(GitHub 不会把 #COMMENT_ID 解析成 comment 跳转)
252+ gh pr comment PR_NUMBER --body " Fixed — <简述修改内容>(回复 [评论](https://github.com/OWNER/REPO/pull/PR_NUMBER#issuecomment-ISSUE_COMMENT_ID))"
253+
254+ # PR description summary 条目(来自 3c)— 同样在 PR 主时间线回复
255+ gh pr comment PR_NUMBER --body " Addressed — <简述修改内容>"
205256```
206257
207- - Resolve 该 thread:
258+ - Resolve 该 thread(仅 inline review thread 适用,issue comment 和 description summary 无 thread 可 resolve) :
208259
209260``` bash
210261gh api graphql -f query=' mutation {
@@ -219,11 +270,15 @@ gh api graphql -f query='mutation {
2192701 . 回复评论说明原因:
220271
221272``` bash
273+ # inline review comment(来自 3a)
222274gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_DATABASE_ID/replies \
223275 -f body=" Not an issue — <具体解释,引用代码说明 reviewer 的判断为什么不适用于此场景>"
276+
277+ # PR 顶层 issue comment / description summary(来自 3b/3c)
278+ gh pr comment PR_NUMBER --body " Not an issue — <具体解释>"
224279```
225280
226- 2 . Resolve 该 thread:
281+ 2 . Resolve 该 thread(仅 inline review thread 适用) :
227282
228283``` bash
229284gh api graphql -f query=' mutation {
@@ -246,10 +301,12 @@ gh api graphql -f query='mutation {
246301- 输出 "🔄 第 N 轮:修复 X 个 CI 问题 + Y 个 review 问题,反驳 Z 个误报,等待新一轮 checks..."
247302- 回到 Step 1
248303
249- ** 如果只有误报被 resolve(无代码修复)且 CI 全部通过:**
250- - 输出 "✅ 第 N 轮:反驳 Y 个误报并 resolve ,所有 CI checks 通过"
304+ ** 如果本轮所有 review 反馈都已处理(inline 已 reply+resolve、3b/3c 已 reply)且无代码修复且 CI 全部通过:**
305+ - 输出 "✅ 第 N 轮:处理 Y 个 review 反馈(含 Z 个反驳) ,所有 CI checks 通过"
251306- 结束循环
252307
308+ > 注意:3b/3c 没有 thread 可以 resolve,"已处理"的标准是已经发出 ` gh pr comment ` 回复。不要因为"没有 resolve 动作"就误判为未处理而陷入死循环。
309+
253310---
254311
255312## 完成汇总
0 commit comments