fix: stop bot from replying after human expert intervention in discussion thread#108
Conversation
…eplied Once a member of SKIP_AUTHORS comments on a discussion thread, the bot treats it as human-intervened and stops processing that thread entirely (including any subsequent follow-up questions from regular users). Also adds yao0928 to SKIP_AUTHORS alongside nevstop. Fixes: too many bot interventions on discussions like orgs/NEVSTOP-LAB/discussions/106
nevstop
left a comment
There was a problem hiding this comment.
Review: copilot/modify-filter-logic → PR #108
Verdict: ship as-is — clean change, well-tested, no blocking issues.
正向确认
✅ compute_reply_plan early exit 位置正确:在 bot_login=None 安全检查和 follow-up 检测之前,因为人工介入判断不依赖 bot_login(只看 author.login)
✅ 空 comments 安全:any() 对空列表返回 False,无崩溃风险
✅ 与 router 互补不冲突:router 的 main() SKIP_AUTHORS 检查按 comment_author 跳过;compute_reply_plan 按 thread 级跳过。两者独立生效,前者节省分类开销,后者覆盖扫描模式
✅ 测试覆盖完整:
- 已有的 2 个 nevstop 测试更新为 thread 级跳过
- 新增 5 个测试:yao0928、bot 前介入、bot_login=None × 2(nevstop + yao0928)
test_compute_reply_plan_skips_when_expert_comments_before_bot覆盖"专家抢在 Bot 前回答"场景,这是关键 edge case
✅ 向后兼容:followup_user_indices中的 SKIP_AUTHORS 过滤保留(在无人工介入时仍有效),非破坏性
Nits
scripts/_utils.pyL25: "Bot 即视为人工已介入,不再对该 thread 进行任何回复(包括后续用户的追问)" — 此描述准确对应compute_reply_plan的行为,但 router 的 JOIN/OTHER 路径不走compute_reply_plan,不受此约束。可考虑加一句"(仅在compute_reply_plan中)"限定范围,避免未来误解。当前不影响功能,可忽略。
When any csm-committee member comments on a discussion thread, the
bot now treats it as human expert intervention and stops replying —
same behavior as the hardcoded SKIP_AUTHORS (nevstop, yao0928).
Changes:
- _github.py: add rest_get() method to GitHubGraphQL for REST API calls
- _utils.py: update comment to note csm-committee dynamic resolution
- discussion_bot.py:
- add CSM_COMMITTEE_SLUG, _team_members_cache, resolve_skip_logins()
- resolve_skip_logins() fetches team members via REST API (cached),
merges with SKIP_AUTHORS
- compute_reply_plan() now accepts optional skip_logins parameter;
defaults to SKIP_AUTHORS for backward compat
- _process_discussion_dict() resolves skip_logins once per discussion
- router.py:
- _handle_qa imports resolve_skip_logins, resolves effective skip set
- passes skip_logins to compute_reply_plan
- tests: 6 new tests for custom skip_logins, resolve_skip_logins,
API failure fallback, case insensitivity, and caching
nevstop
left a comment
There was a problem hiding this comment.
Review: copilot/modify-filter-logic — csm-committee integration
Verdict: ship as-is — clean implementation, well-tested, no blocking issues.
正向确认
✅ rest_get() 实现正确:auth headers 与 query() 一致,HTTPError → RuntimeError 统一
✅ resolve_skip_logins 三级降级:API 正常 → 合并;响应非 list → warning + 空缓存;异常 → exception log + 空缓存。降级后 SKIP_AUTHORS 始终生效
✅ 缓存设计明智:首次调用按 org/slug 缓存,后续调用零 API 开销;进程重启自动刷新
✅ compute_reply_plan(skip_logins=) 向后兼容:None 时退化为 SKIP_AUTHORS,现有调用方无需修改;effective_skip 同时用于 early exit 和 followup_user_indices 过滤,一致
✅ router main() 保持轻量:仅用 SKIP_AUTHORS 快检,不引入 discussion_bot 依赖;csm-committee 的完整检测在 _handle_qa 内部通过 resolve_skip_logins 完成
✅ 测试覆盖全面:custom skip_logins(含 casefold、空集合)、resolve_skip_logins(失败回退、合并、缓存命中)
✅ 无安全风险:path 参数由 _get_source_repo_parts() + 常量拼接,不受用户输入控制
Nits(忽略级,不影响合入)
_github.pyL77rest_get返回类型list | dict偏窄(json.loads实际可返回int/str/bool/None)。不建议改为Any,但可加注释说明实际端点总是 list/dict。discussion_bot.pyL93_team_members_cache.get(cache_key, frozenset())— 此行在if cache_key not in块之后,get的 default 永不会使用,属于无害防御代码。discussion_bot.pyrest_get无分页处理:?per_page=100对当前 csm-committee 规模足够;若团队超过 100 人需加?page=循环。当前可加# TODO:注释。_utils.pyL27 "不再对该 thread 进行任何回复" — 准确描述了compute_reply_plan行为;router 的 JOIN/OTHER 路径不经过此函数,但当前场景(csm-committee 成员不太可能触发 JOIN)无实际影响。
Once
nevstop,yao0928, or anycsm-committeeteam member comments on a Q&A discussion thread, the bot stops replying to subsequent follow-up questions from regular users — treating it as human expert intervention (e.g., discussions/106).Changes
Static skip list
scripts/_utils.py— Addyao0928toSKIP_AUTHORS(previously onlynevstop)Dynamic csm-committee resolution
scripts/_github.py— Addrest_get(path)method toGitHubGraphQLfor REST API callsscripts/discussion_bot.py:CSM_COMMITTEE_SLUG = "csm-committee"and module-level team members cacheresolve_skip_logins(client, org)— fetches csm-committee members viaGET /orgs/{org}/teams/csm-committee/members, merges withSKIP_AUTHORS, caches resultcompute_reply_plan()now accepts optionalskip_loginsparameter (defaults toSKIP_AUTHORSfor backward compat)_process_discussion_dict()resolves skip_logins once per discussion and passes tocompute_reply_planscripts/router.py—_handle_qaimportsresolve_skip_logins, resolves effective skip set, and passesskip_loginstocompute_reply_planThread-level skip
scripts/discussion_bot.py— Add early-exit incompute_reply_plan: if any skip-list member has commented in the thread, returnNoneimmediately regardless of subsequent activityTests
tests/test_discussion_bot.py— Update 2 tests for thread-level skip; add 11 new tests covering:yao0928and pre-bot expert commentsbot_login=Nonescenarios (nevstop + yao0928)skip_loginsparameter (case-insensitive, empty set)resolve_skip_logins(API failure fallback, team merge, caching)