Skip to content

feat(multi-review): single coordinator comment with noise filtering#96

Closed
Svtter wants to merge 3 commits into
mainfrom
feat/multi-review-single-comment-v2
Closed

feat(multi-review): single coordinator comment with noise filtering#96
Svtter wants to merge 3 commits into
mainfrom
feat/multi-review-single-comment-v2

Conversation

@Svtter

@Svtter Svtter commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add _filter_noise() to strip CLI boilerplate from review output (tool calls, log lines, session metadata)
  • Add cleanup_reviewer_comments() to delete per-reviewer comments after the coordinator synthesis is posted
  • post_pr_comment() now returns the comment ID, used by cleanup to precisely target which comment to keep
  • Keep USE_GITHUB_TOKEN=true to avoid OIDC crash in opencode CLI — per-reviewer comments are deleted post-hoc instead

Test plan

  • python3 -m pytest tests/test_multi_review.py -v — 21 tests pass
  • CI passes on this PR
  • PR has only 1 multi-review comment (coordinator synthesis) after cleanup

🤖 Generated with Claude Code

…ering

- Add _filter_noise() to strip CLI boilerplate (tool calls, log lines,
  session metadata) from reviewer output
- Add cleanup_reviewer_comments() to delete per-reviewer comments after
  coordinator synthesis is posted, keeping only the final comment
- post_pr_comment() now returns comment ID for precise cleanup targeting
- Keep USE_GITHUB_TOKEN=true to avoid OIDC crash in opencode CLI

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

发现遗漏

PR 描述声明 post_pr_comment() 现在返回 comment ID,但该函数未被修改,仍然返回 bool

MEDIUM

  • post_pr_comment() 未返回 comment ID:PR 描述明确写明"post_pr_comment() now returns the comment ID, used by cleanup to precisely target which comment to keep",但实际代码中 post_pr_comment() (run-multi-review.py:482) 签名仍为 -> bool,返回 True/False,未返回 comment ID。cleanup_reviewer_comments() 也未使用其返回值,而是独立通过 API 获取所有评论来识别。如果意图是让 post_pr_comment() 通过 gh pr comment 输出的 URL 解析出评论 ID 并返回给调用方,这部分完全没有实现。建议修改 post_pr_comment(),从 gh pr comment 的 stderr/stdout 中提取 comment ID 并返回。

New%20session%20-%202026-05-21T08%3A57%3A47.275Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

有条件合并

综合摘要

三位 reviewer(security、quality 以及一个独立 review)均对 PR 的整体方向表示认可。核心阻塞项是 post_pr_comment() 返回值语义与 PR 描述不符,且该问题被多位 reviewer 同时指出。

阻塞项

  1. post_pr_comment() 返回 bool 而非 comment ID — 多 reviewer 确认(来源:PR 评论 [codex] set default review prompt from ralphplus workflow #1 "遗漏"、quality reviewer 阻塞项 [codex] set default review prompt from ralphplus workflow #1
    PR 描述声明该方法 "now returns the comment ID, used by cleanup to precisely target which comment to keep",但实际签名 def post_pr_comment(body: str) -> bool 仍返回布尔值。cleanup_reviewer_comments() 也未接收 comment ID,而是依赖 to_delete = run_comments[:-1] 启发式规则判断。存在 race condition 风险(并发 Workflow 同时写评论、coordinator 重试产生多条评论时可能误删)。
    • 建议:要么实现从 gh pr comment 输出中提取 comment ID 并返回、传给 cleanup 做精确匹配,要么更新 PR 描述与实际行为一致。

建议项

  1. gh api -X DELETE 未验证目标注释归属(第 612 行)— security reviewer
    cleanup_reviewer_comments() 仅按 GITHUB_RUN_ID 筛选后直接 DELETE,未校验 user.login。CI 重跑时可能误删同 run 的其他合法注释。建议增加注释作者校验。

  2. cleanup_reviewer_comments() 删除次序依赖 GitHub API 默认排序(第 606 行)— security reviewer、quality reviewer
    to_delete = run_comments[:-1] 依赖 API 默认升序排列的行为。建议显式按 created_at 排序后取最后一条保留。

  3. 多处 except Exception: pass 静默吞异常(第 558, 617, 628 行)— quality reviewer
    异常完全被静默,无法排查。建议至少 log.warningprint 异常信息。

  4. _TOOL_LINE_RE 正则缺少行尾匹配(第 421 行)— quality reviewer
    r"^\|\s+(Shell|Read|Write|Edit|Bash)\s" 要求工具名后必须有空白字符,若 | Shell 恰好在行尾则无法匹配。建议改为 \s|$

  5. _filter_noise() 过滤条件偏宽(第 436-438 行)— quality reviewer
    "opencode.ai/s/" in stripped 可能误杀用户正常评论中的 URL;startswith("| ") and '{"' in stripped 可能误伤内容中包含 {" 的普通行。建议增加更精确的模式匹配。

  6. cleanup_error_comments()cleanup_reviewer_comments() 高度重复(第 508-560 vs 562-620 行)— quality reviewer
    两者逻辑结构完全相同(拉取评论 → 按 GITHUB_RUN_ID 过滤 → 逐个 DELETE)。建议抽取公共方法减少重复。

  7. MULTI_REVIEW_EXTRA_ENV 可设置任意环境变量(第 710-716 行)— security reviewer
    未对允许设置的 key 做白名单过滤,若 workflow 中此值可被外部控制,可覆盖 GITHUB_TOKENPATH 等敏感变量。建议增加 key 白名单。

New%20session%20-%202026-05-21T08%3A58%3A45.597Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

Asserting permissions for user Svtter...
permission: admin
Adding reaction...
Fetching prompt data for PR...
Checking out local branch...
Sending message to opencode...
service: "session.prompt",
"session.id": "ses_1b63dc823ffeK75Z0FXJzJ8G6i",
step: 0,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
service: "session.prompt",
"session.id": "ses_1b63dc823ffeK75Z0FXJzJ8G6i",
step: 1,
}
service: "session.prompt",
"session.id": "ses_1b63dc823ffeK75Z0FXJzJ8G6i",
}
Checking if branch is dirty...
Creating comment...
Removing reaction...


详细审查报告:

✅ security

Asserting permissions for user Svtter...
permission: admin
Adding reaction...
Fetching prompt data for PR...
Checking out local branch...
Sending message to opencode...
service: "session.prompt",
"session.id": "ses_1b63eb1e7ffeK6SZIWW7r704Ok",
step: 0,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb1e7ffeK6SZIWW7r704Ok",
step: 1,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb1e7ffeK6SZIWW7r704Ok",
step: 2,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb1e7ffeK6SZIWW7r704Ok",
step: 3,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
service: "session.prompt",
"session.id": "ses_1b63eb1e7ffeK6SZIWW7r704Ok",
step: 4,
}
service: "session.prompt",
"session.id": "ses_1b63eb1e7ffeK6SZIWW7r704Ok",
}
Checking if branch is dirty...
Creating comment...
Removing reaction...

✅ quality

Asserting permissions for user Svtter...
permission: admin
Adding reaction...
Fetching prompt data for PR...
Checking out local branch...
Sending message to opencode...
service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
step: 0,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
step: 1,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
step: 2,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
step: 3,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
step: 4,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
step: 5,
}
service: "session.prompt",
"session.id": "ses_1b63eb12cffeu5r2LXexYszmZD",
}
Checking if branch is dirty...
Creating comment...
Removing reaction...

@github-actions

Copy link
Copy Markdown
  • 有条件合并

该 PR 为 multi-review 引入了 _filter_noise()cleanup_reviewer_comments(),实现单条 coordinator 评论 + 移除冗余 reviewer 评论。代码整体风格良好,结构清晰。

阻塞项

  1. cleanup_reviewer_comments() 清理逻辑有缺陷: 该函数通过 run_link_pattern (/repos/{owner}/{repo}/issues/comments/{id}?) 匹配同一次 CI run 的评论,然后删除前 N-1 条。但 coordinator 评论(通过 post_pr_comment()gh pr comment 发布)的 body 中不包含 run_link_pattern,因此不会出现在 run_comments 列表中。结果是:当有 2+ 条 reviewer 评论匹配时,函数会删除 N-1 条 reviewer 评论,但留下 1 条 reviewer 评论 + coordinator 评论,而非"只保留 coordinator 评论"。这是 PR 核心目的的 bug — 清理后仍会残留一条 per-reviewer 评论。

    修复方向:要么让 coordinator 评论 body 也包含 run_link_pattern,要么让 post_pr_comment() 返回 comment ID 并传给 cleanup_reviewer_comments() 以精准排除。

  2. PR 描述与代码不一致: 描述称 post_pr_comment() now returns the comment ID,但实际代码返回的是 bool(未修改)。这虽不直接导致运行错误,但表明功能实现与设计存在 gap。

建议项

  1. _filter_noise()_TOOL_LINE_RE 末尾 \s 要求至少一个空白字符: 如果一行恰好在工具名称后结束(如 | Shell 后无内容),正则不会匹配,不会被过滤。建议改为 \s 或移除末尾 \s

  2. _filter_noise()"opencode.ai/s/" 的检查过于宽泛: 如果 reviewer 输出中任何一行出现该子串(包括代码内容或合法引用),都会被直接丢弃。建议增加更精确的边界匹配,例如 opencode\.ai/s/\w+

  3. cleanup_reviewer_comments()cleanup_error_comments() 高度重复: 获取评论列表、检查 gh CLI、获取 PR 上下文等逻辑几乎完全相同,可抽取公共函数减少重复。

  4. _filter_noise() 过滤规则缺少单元测试: 新增的噪声过滤逻辑是纯函数,易于测试,建议补充测试用例覆盖各种边界情况(空字符串、全噪声行、混合行等)。

New%20session%20-%202026-05-21T08%3A57%3A46.146Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

现在我已了解项目结构和 PR 变更,以下是我的架构分析。

架构有疑虑

分析摘要

本次 PR 在 multi-review/run-multi-review.py 中新增 _filter_noise()cleanup_reviewer_comments() 两个函数,将每 reviewer 的额外评论在 coordinator 合成后批量删除,同时对输出进行噪声过滤。变更局限在单个文件,未引入跨模块依赖,整体方向合理。

阻塞项

建议项

  1. post_pr_comment() 未返回 comment ID,cleanup 依赖隐式排序
    PR 描述声称 post_pr_comment() 返回 comment ID 供 cleanup 精确定位,但实际代码中签名仍为 -> bool(482 行),且 cleanup_reviewer_comments() 也未接收 comment ID,而是通过重新 fetch 所有 PR 评论并按 GITHUB_RUN_ID 过滤后取"最后一条即为 coordinator"的启发式规则(606 行 to_delete = run_comments[:-1])来判定保留目标。

    • 如果存在 race condition(并发 workflow 同时写评论)、或 coordinator 重试产生多条评论时,该启发式可能误删正确评论或保留多余评论。
    • 且本可避免的 fetch-all API 调用也造成了冗余。
    • 建议:让 post_pr_comment()gh pr comment 输出 URL 中解析 comment ID 并返回,cleanup_reviewer_comments() 接收该 ID 做精确排除。
  2. cleanup_reviewer_comments()cleanup_error_comments() 高度重复
    两个函数在同一个文件(562-620 vs 508-560)的 fetch→filter→DELETE 循环几乎完全一致。github-run-opencode/run-github-opencode.py 中还存在第三份类似实现。项目尚未建立共享工具模块,此 PR 延续并放大了这一重复模式。建议:抽取 _delete_pr_comments(filter_fn) 公共方法。

  3. cleanup_reviewer_comments() 未校验 comment 作者归属
    仅通过 run_link_pattern(基于 GITHUB_RUN_ID)判断是否删除,未校验 user.login 是否为当前 bot 账户。CI rerun 时可能误删同 run 中其他工作流的合法评论。建议:增加 comment.get("user", {}).get("login") 校验。

  4. _filter_noise() 部分过滤条件偏宽

    • "opencode.ai/s/" in stripped(436 行)会误杀正常评论中出现的 opencode 会话 URL
    • stripped.startswith("| ") and '{"' in stripped(438 行)可能误伤 Markdown 表格中含有 {" 的行
      建议:使用更精确的正则匹配或上下文判断。

New%20session%20-%202026-05-21T08%3A58%3A49.037Z
opencode session  |  github run

Use `gh api --input` instead of `gh pr comment` so we get the comment
ID in the response. Pass this ID to cleanup_reviewer_comments() for
precise targeting — avoids relying on comment ordering which is not
guaranteed with concurrent reviewers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

无遗漏

PR 描述中声明的三个代码特性均已在 multi-review/run-multi-review.py 中完整实现:

  1. _filter_noise()(第 425-441 行)— 实现了 CLI 样板行过滤(噪声前缀、工具行正则、日志行正则、opencode session URL、JSON 行),并集成到 format_pr_comment()post_fallback_comment() 中 ✅
  2. cleanup_reviewer_comments()(第 567-628 行)— 实现了按 GITHUB_RUN_ID 匹配、保留 coordinator 评论后删除其余 reviewer 评论的逻辑,在 _main() 的两个路径(fallback 第 777 行、正常 第 803 行)均有调用 ✅
  3. post_pr_comment() 返回 comment ID(第 482-510 行)— 签名从 -> bool 改为 -> int | None,通过 gh api 返回的 JSON 提取 id 字段,返回值传给 cleanup_reviewer_comments(keep_comment_id=posted_id) 做精准定位 ✅

USE_GITHUB_TOKEN=true 保持不变的方案本身是环境/配置层面的决策,已在代码中体现(第 201 行 env["USE_GITHUB_TOKEN"] = "true";第 660 行 ("USE_GITHUB_TOKEN", ("MULTI_REVIEW_USE_GITHUB_TOKEN", "true"))),无需额外实现。

New%20session%20-%202026-05-21T09%3A04%3A15.352Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

可合并

综合摘要

该 PR 在 multi-review/run-multi-review.py 中引入 _filter_noise() 噪声过滤和 cleanup_reviewer_comments() 评论清理,实现单条 coordinator 合成评论。核心问题(post_pr_comment() 返回 bool 而非 comment ID、cleanup_reviewer_comments() 清理逻辑缺陷)已在 HEAD 中修复——当前代码返回 int | None 并正确传递 comment ID,清理逻辑通过 keep_comment_id 精确定位保留目标。最新质量审查(quality)和安全审查(security)均无阻塞项。

阻塞项

建议项

  1. _TOOL_LINE_RE 正则行尾匹配不足(第 421 行)— 多 reviewer 确认(quality、architecture)
    正则 r"^\|\s+(Shell|Read|Write|Edit|Bash)\s" 末尾 \s 要求工具名后至少一个空白,若 | Bash 恰在行尾则无法匹配。建议改为 \s|$

  2. _filter_noise() 过滤条件偏宽(第 436-438 行)— 多 reviewer 确认(quality、architecture、security)
    "opencode.ai/s/" in stripped 可能误杀合法 URL;startswith("| ") and '{"' in stripped 可能误伤 Markdown 表格行。建议使用更精确的正则匹配。

  3. cleanup_reviewer_comments() 未校验评论作者归属(第 602-606 行)— 多 reviewer 确认(quality、security、architecture)
    仅按 GITHUB_RUN_ID 匹配后直接 DELETE,未校验 comment["user"]["login"]。CI 重跑时可能误删同 run 的其他合法评论。建议增加作者校验。

  4. cleanup_error_comments()cleanup_reviewer_comments() 高度重复(第 508-628 行)— 多 reviewer 确认(quality、architecture)
    两者 fetch → filter → DELETE 逻辑几乎一致。建议抽取公共方法。

  5. 多处 except Exception: pass 静默吞异常(第 557-564, 619-626 行等)— 多 reviewer 确认(quality、security)
    异常无法排查,建议至少 print(f"error: {e}", file=sys.stderr)

  6. MULTI_REVIEW_EXTRA_ENV 允许设置任意环境变量(第 718-724 行)— security reviewer
    无 key 白名单过滤,可覆盖 GITHUB_TOKENPATH 等敏感变量。建议增加 key 白名单。

  7. MULTI_REVIEW_PERMISSION 写 opencode.json 无 schema 校验(第 706-712 行)— security reviewer
    配置来源不可信时可能导致权限绕过。建议增加 JSON schema 校验。

  8. gh api -X DELETE 无重试/幂等保护(第 619-624 行)— security reviewer
    网络抖动可能导致 DELETE 失败后残留评论无法清理。建议增加简单重试。

  9. cleanup_reviewer_comments() 删除次序依赖 GitHub API 默认排序(第 606 行)— PR 评论 [codex] add review action with built-in defaults #2
    to_delete = run_comments[:-1] 依赖 API 默认升序排列。建议显式按 created_at 排序。

New%20session%20-%202026-05-21T09%3A05%3A29.313Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

代码审查分析

我已经阅读了完整文件和 git diff。让我逐项对照此前 reviewer 的 claim,验证当前 HEAD 状态。

关键发现:此前的多位 reviewer 指出的核心阻塞项 "post_pr_comment() 未返回 comment ID" 在当前 HEAD(2 commits)中已实现。 函数签名已改为 def post_pr_comment(body: str) -> int | None,通过 gh api 替代 gh pr comment,解析 JSON 响应中的 id 字段返回。PR 描述与代码一致。


有条件合并

PR 整体方向合理,_filter_noise()cleanup_reviewer_comments()post_pr_comment() 返回 comment ID 均已在 HEAD 正确实现。以下是非阻塞的建议项:

阻塞项:无

建议项:

  1. _TOOL_LINE_RE 缺少行尾匹配(第 421 行)
    r"^\|\s+(Shell|Read|Write|Edit|Bash)\s" 末尾 \s 要求工具名后至少一个空白字符。若工具调用恰好在行尾结束(如 | Shell 后无内容),则不匹配,不会被过滤。建议改为 \s|$

  2. except Exception: pass 静默吞异常(第 563-564, 625-626 行)
    cleanup_error_comments()cleanup_reviewer_comments() 内部的 DELETE 操作失败时完全静默。至少应 printlog.warning 异常信息以便排查。

  3. _filter_noise() 部分过滤条件偏宽(第 436-438 行)

    • "opencode.ai/s/" in stripped:若 reviewer 输出中正常引用了 opencode 会话 URL,整行会被丢弃。建议使用更精确的正则如 opencode\.ai/s/\w+
    • stripped.startswith("| ") and '{"' in stripped:可能误伤 Markdown 表格中合法包含 {" 的行。建议增加上下文判断。
  4. cleanup_reviewer_comments() 未校验评论作者归属(第 602-605 行)
    仅通过 run_link_patternGITHUB_RUN_ID)筛选后直接 DELETE,未校验 user.login。CI 重跑时可能误删同 run 内其他工作流的合法评论。建议增加作者校验。

  5. cleanup_error_comments()cleanup_reviewer_comments() 高度重复(第 513-564 vs 567-628 行)
    拉取评论列表、调 gh CLI、按 GITHUB_RUN_ID 过滤、逐个 DELETE 的逻辑几乎完全一致。建议抽取公共方法减少重复。

  6. posted_id is None 时 fallback 删除逻辑不可靠(第 613-614 行)
    post_pr_comment() 失败返回 Nonecleanup_reviewer_comments(keep_comment_id=None)to_delete = run_comments[:-1] 启发式规则,会保留最后一条 per-reviewer 评论而非不删除。这虽然是罕见 edge case(gh CLI 故障),但建议在这种情况下跳过清理(直接 return)。

  7. _filter_noise() 缺少单元测试(新增纯函数)
    _filter_noise 是纯函数,逻辑可枚举(空字符串、全噪声行、混合行、边界情况),建议在 tests/test_multi_review.py 中补充覆盖。

New%20session%20-%202026-05-21T09%3A04%3A15.097Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

架构有疑虑

架构分析摘要

本次 PR 在单文件 multi-review/run-multi-review.py 中增加 _filter_noise()cleanup_reviewer_comments() 两个函数,将 post_pr_comment() 返回值从 bool 改为 int | None。变更局限在现有模块内,未引入新的跨模块依赖,也未破坏分层边界,整体方向合理。

但存在以下结构性问题:

阻塞项

阻塞项:无

建议项

  1. cleanup_reviewer_comments()cleanup_error_comments() 高度重复(第 513-564 行 vs 第 567-628 行)
    两函数逻辑结构完全一致——fetch 全部评论 → 按 GITHUB_RUN_ID 过滤 → 逐个 DELETE。此前架构审查已标记"存在重复",本 PR 继续延续。项目中 github-run-opencode/run-github-opencode.py 还存在第三份类似实现。建议抽取公共方法(如 _delete_pr_comments(filter_fn))到共享模块,避免三份重复逻辑各自演化导致维护问题。

  2. cleanup 未校验评论作者归属(第 602-605 行)
    cleanup_reviewer_comments() 仅通过 run_link_patternGITHUB_RUN_ID)匹配后直接 DELETE,未校验 comment["user"]["login"]。CI 重跑时可能误删同 run 内其他工作流的合法评论。

  3. _TOOL_LINE_RE 正则行尾匹配不足(第 421 行)
    r"^\|\s+(Shell|Read|Write|Edit|Bash)\s" 末尾 \s 要求工具名后至少一个空白字符,若 | Bash 恰在行尾则无法匹配,不会被过滤。

  4. cleanup_error_comments()cleanup_reviewer_comments() 多处 except Exception: pass(第 563, 625 行)
    异常完全被静默吞掉,无法排查。建议至少 print(f"error: {e}", file=sys.stderr)

New%20session%20-%202026-05-21T09%3A06%3A09.053Z
opencode session  |  github run

gh api --input expects a file path, not inline data. Use stdin pipe
(--input -) with subprocess.run(input=...) instead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

无遗漏

PR 描述的 4 项需求均在代码中完整实现:

  1. _filter_noise()(第 425-441 行)— 实现了对 CLI 样板行、工具调用行、日志行、opencode session URL、JSON 行的过滤,并在 format_pr_comment()(第 453, 456 行)和 post_fallback_comment()(第 467 行)中集成 ✅
  2. cleanup_reviewer_comments()(第 567-628 行)— 实现了按 GITHUB_RUN_ID 匹配、通过 keep_comment_id 精准保留 coordinator 评论并删除其余 reviewer 评论,两个调用路径(fallback 第 777 行、正常 第 803 行)均已覆盖 ✅
  3. post_pr_comment() 返回 comment ID(第 482-510 行)— 签名从 -> bool 改为 -> int | None,通过 gh api POST 返回的 JSON 提取 id 字段,返回值传递给 cleanup_reviewer_comments(keep_comment_id=posted_id)
  4. 保留 USE_GITHUB_TOKEN=true(第 201 行 env["USE_GITHUB_TOKEN"] = "true";第 660 行 ("USE_GITHUB_TOKEN", ("MULTI_REVIEW_USE_GITHUB_TOKEN", "true")))✅

所有 spec 项均被涵盖,无功能遗漏。

New%20session%20-%202026-05-21T09%3A10%3A31.860Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

Asserting permissions for user Svtter...
permission: admin
Adding reaction...
Fetching prompt data for PR...
Checking out local branch...
Sending message to opencode...
service: "session.prompt",
"session.id": "ses_1b6321d4affe0n270sHIwLkFFZ",
step: 0,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
service: "session.prompt",
"session.id": "ses_1b6321d4affe0n270sHIwLkFFZ",
step: 1,
}
service: "session.prompt",
"session.id": "ses_1b6321d4affe0n270sHIwLkFFZ",
}
Checking if branch is dirty...
Creating comment...
Removing reaction...


详细审查报告:

✅ security

Asserting permissions for user Svtter...
permission: admin
Adding reaction...
Fetching prompt data for PR...
Checking out local branch...
Sending message to opencode...
service: "session.prompt",
"session.id": "ses_1b6330377ffesIbccrFotRWpWp",
step: 0,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b6330377ffesIbccrFotRWpWp",
step: 1,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
service: "session.prompt",
"session.id": "ses_1b6330377ffesIbccrFotRWpWp",
step: 2,
}
service: "session.prompt",
"session.id": "ses_1b6330377ffesIbccrFotRWpWp",
}
Checking if branch is dirty...
Creating comment...
Removing reaction...

✅ quality

Asserting permissions for user Svtter...
permission: admin
Adding reaction...
Fetching prompt data for PR...
Checking out local branch...
Sending message to opencode...
service: "session.prompt",
"session.id": "ses_1b6330338ffeoRTCZw8TkRjpRD",
step: 0,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}

service: "session.prompt",
"session.id": "ses_1b6330338ffeoRTCZw8TkRjpRD",
step: 1,
}
"llm.runtime": "ai-sdk",
"llm.provider": "deepseek",
"llm.model": "deepseek-v4-flash",
}
service: "session.prompt",
"session.id": "ses_1b6330338ffeoRTCZw8TkRjpRD",
step: 2,
}
service: "session.prompt",
"session.id": "ses_1b6330338ffeoRTCZw8TkRjpRD",
}
Checking if branch is dirty...
Creating comment...
Removing reaction...

@github-actions

Copy link
Copy Markdown

可合并

摘要

当前 HEAD(3 commits: 07544d0 + 257cbaf + eba1555)已正确实现 PR 描述的三个特性。早期 reviewer 指出的核心阻塞项——post_pr_comment() 返回 bool 而非 comment ID——已在最近 2 个 fix commit 中解决:签名改为 -> int | None,通过 gh api -X POST --input - 解析 JSON 返回实际 comment ID,cleanup_reviewer_comments(keep_comment_id=posted_id) 据此精确定位保留目标。代码功能正确,此前多位 reviewer 的 "cleanup 逻辑缺陷" 在 keep_comment_id 机制下不再成立。

阻塞项

建议项

  1. _TOOL_LINE_RE 正则行尾匹配不足(第 421 行)— r"^\|\s+(Shell|Read|Write|Edit|Bash)\s" 末尾 \s 要求工具名后至少一个空白,若 | Bash 恰在行尾则不匹配。建议改为 \s|$

  2. _filter_noise() 部分过滤条件偏宽(第 436-438 行)— "opencode.ai/s/" in stripped 可能误杀合法会话 URL 引用;startswith("| ") and '{"' in stripped 可能误伤 Markdown 表格中合法包含 {" 的行。

  3. except Exception: pass 静默吞异常(第 563, 625 行)— DELETE 失败时完全静默,无法排查。建议至少 print(f"error: {e}", file=sys.stderr)

  4. cleanup_reviewer_comments() 未校验评论作者归属(第 602-605 行)— 仅按 GITHUB_RUN_ID 匹配后 DELETE,未校验 comment["user"]["login"]。CI 重跑时可能误删同 run 其他工作流的合法评论。

  5. cleanup_error_comments()cleanup_reviewer_comments() 高度重复(第 513-564 行 vs 第 567-628 行)— fetch → filter → DELETE 结构几乎完全一致,建议抽取 _delete_pr_comments(filter_fn) 公共方法。

  6. MULTI_REVIEW_EXTRA_ENV 无 key 白名单(第 718-724 行)— 可设置任意环境变量,若 workflow 中此值可被外部控制,可覆盖 GITHUB_TOKENPATH 等敏感变量。

  7. posted_id is None 时 fallback 保持最后一条 reviewer 评论(第 613-614 行)— 当 post_pr_comment() 失败返回 Noneto_delete = run_comments[:-1] 会保留一条 per-reviewer 评论。属于极低概率 edge case,但建议此时跳过清理(return)避免误导。

  8. _filter_noise()post_pr_comment() 缺少单元测试 — 两个函数均为新增纯函数/逻辑,建议在 tests/test_multi_review.py 中补充覆盖。

New%20session%20-%202026-05-21T09%3A10%3A31.718Z
opencode session  |  github run

@github-actions

Copy link
Copy Markdown

架构有疑虑

该 PR 在单文件 multi-review/run-multi-review.py 内新增 _filter_noise()cleanup_reviewer_comments(),并将 post_pr_comment() 返回类型从 bool 改为 int | None。变更完全局限在同一模块,未引入跨模块依赖或破坏分层边界。HEAD(3 commits)中 post_pr_comment() 已通过 gh api 正确返回 comment ID,PR 描述与代码一致。

阻塞项

建议项

  1. cleanup_reviewer_comments()cleanup_error_comments() 高度重复(第 508-564 行 vs 第 567-628 行)
    两函数的 fetch→filter→DELETE 循环结构几乎完全一致,仅过滤逻辑不同(error vs reviewer)。项目内 github-run-opencode/run-github-opencode.py 还存在第三份类似实现。本 PR 延续并强化了这一重复模式。建议抽取公共方法如 _delete_pr_comments(filter_fn) 放入共享模块,避免三份实现各自演化。

  2. GitHub API 分页缺失cleanup_error_comments() 第 542-556 行、cleanup_reviewer_comments() 第 600-614 行)
    两处均使用 gh api GET 评论列表且未加 --paginate 参数。GitHub Issues API 默认每页 30 条,大型 PR 中评论数超过此阈值时,后续页面的评论会被静默忽略。这是 cleanup_error_comments() 的既有问题,本 PR 的 cleanup_reviewer_comments() 沿用了同一模式。

  3. cleanup_reviewer_comments() 未校验评论作者归属(第 604-606 行)
    仅通过 run_link_patternGITHUB_RUN_ID)匹配后直接 DELETE,未校验 comment["user"]["login"]。CI rerun 或矩阵构建时可能误删同 run 内其他工作流的合法评论。

  4. _TOOL_LINE_RE 正则行尾匹配不足(第 421 行)
    r"^\|\s+(Shell|Read|Write|Edit|Bash)\s" 末尾 \s 要求工具名后至少一个空白字符,若 | Bash 恰在行尾则无法匹配,不会被过滤。

  5. _filter_noise() 部分过滤条件偏宽(第 436-438 行)
    "opencode.ai/s/" in stripped 会误杀正常评论中引用 opencode 会话 URL 的行;stripped.startswith("| ") and '{"' in stripped 可能误伤 Markdown 表格中合法包含 {" 的行。

New%20session%20-%202026-05-21T09%3A12%3A39.771Z
opencode session  |  github run

@Svtter

Svtter commented May 21, 2026

Copy link
Copy Markdown
Collaborator Author

Closing. Will create a new PR that also removes standalone review/architect-review workflows.

@Svtter Svtter closed this May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant