Skip to content

fix: prevent #N auto-reference conversion in GitHub comments#175

Merged
Svtter merged 1 commit into
mainfrom
fix/hash-reference-issue-173
Jun 3, 2026
Merged

fix: prevent #N auto-reference conversion in GitHub comments#175
Svtter merged 1 commit into
mainfrom
fix/hash-reference-issue-173

Conversation

@Svtter

@Svtter Svtter commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #173

Agent 在输出中使用 #1#2 格式时,GitHub 会自动将其转换为 issue/PR 引用链接,导致语义被改变。

方案:两层防御

1. Prompt 层(预防)

在所有 action 的默认 prompt、reviewer persona、coordinator prompt、语言指令中添加明确指令,告诉 agent 不要使用 #N 格式编号。

涉及文件:

  • review/action.yml
  • architect-review/action.yml
  • feature-missing/action.yml
  • spec-coverage/action.yml
  • multi-review/reviewers/*.yaml(quality, security, performance, architecture)
  • multi-review/src/orchestrator.ts(DEFAULT_COORDINATOR_PROMPT)
  • github-run-opencode/run-github-opencode.py(语言指令追加)
  • multi-review/src/reviewers.ts(语言指令追加)

2. Code 层(兜底)

multi-review/src/platform.tspostPRComment() 中添加 escapeHashReferences() 后处理函数,在发布 PR 评论前自动将 #N 中的 # 和数字之间插入零宽空格(zero-width space),阻止 GitHub 的自动引用转换。

Testing

  • 所有现有测试通过(44 tests, 39 passed, 5 pre-existing failures in TestDogfoodWorkflow due to missing workflow file in worktree)
  • TypeScript 编译通过(npx tsc --noEmit

Two-layer defense against #N patterns being auto-converted to
issue/PR references by GitHub:

1. Prompt layer: Add explicit instructions to all action prompts,
   reviewer personas, coordinator prompt, and language instructions
   telling the agent never to use #N format for numbering items.

2. Code layer: Add escapeHashReferences() post-processing in
   platform.ts that inserts a zero-width space between # and digits
   in agent output before posting as PR comments.

Closes #173
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

我已读取所有变更文件和 PR body。Issue #173 的内容已在 PR body 中完整描述(webfetch 直接拉取该 issue 返回 404,但 PR body 包含了完整的 issue 描述和方案说明)。

分析

Issue 规范(来自 PR body):

  • 问题:Agent 输出中的 #1#2 格式会被 GitHub 自动转为 issue/PR 引用链接,语义被改变。
  • 方案:两层防御
    1. Prompt 层(预防):在所有 action 默认 prompt、reviewer persona、coordinator prompt、语言指令中添加"不要使用 #N 格式"的指令。
    2. Code 层(兜底):在 multi-review/src/platform.tspostPRComment() 中添加 escapeHashReferences(),在 # 和数字之间插入零宽空格。

实现验证:

PR body 涉及文件 实现状态
review/action.yml ✓ 第 112 行含 "IMPORTANT: Never use #N format..."
architect-review/action.yml ✓ 第 285 行含同款指令
feature-missing/action.yml ✓ 第 116 行含同款指令
spec-coverage/action.yml ✓ 第 128 行含同款指令
multi-review/reviewers/quality.yaml ✓ 末尾含同款指令
multi-review/reviewers/security.yaml ✓ 末尾含同款指令
multi-review/reviewers/performance.yaml ✓ 末尾含同款指令
multi-review/reviewers/architecture.yaml ✓ 末尾含同款指令
multi-review/src/orchestrator.ts (DEFAULT_COORDINATOR_PROMPT) ✓ 第 30 行含同款指令
multi-review/src/reviewers.ts (语言指令) ✓ 第 45-49 行 hashAvoidZh/hashAvoidEn 已拼接到 langInstruction
github-run-opencode/run-github-opencode.py (语言指令) ✓ 第 458-466 行 zh_instruction 与 en_hash_instruction 已拼接到 PROMPT
multi-review/src/platform.ts (Code 层兜底) ✓ 第 153-157 行定义 escapeHashReferences(),第 162 行在 postPRComment() 中先转义再分发到 GitHub/Gitea/stdout 各路径

集成完整性:

  • escapeHashReferences()postPRComment() 唯一入口调用,GitHub(postCommentGithub,含 gh CLI 与 REST API fallback)、Gitea(postCommentGitea,含 tea CLI 与 REST API fallback)、stdout 兜底三条路径全部使用 escape 后的 body,闭合完整。
  • 正则 (^|\s)#(\d{1,6})(?=[\s:.]|$) 含行首/空白锚点和前瞻,避免误转义代码块中或合法 hash 后的数字。
  • ​ (zero-width space) 插入位置正确:位于 # 与数字之间,能阻止 GitHub 解析为 issue 引用。

PR body 中列出的所有文件、所有防御层级、所有集成路径均已实现,无缺失。

结论

无遗漏

PR 完整实现了 issue #173 所要求的两层防御方案:所有 12 个 prompt/persona/语言指令文件均加入了 "不要使用 #N 格式" 指令;escapeHashReferences()postPRComment() 中正确插入并贯穿到所有 PR 评论发布路径(GitHub gh CLI、GitHub REST API fallback、Gitea tea、Gitea REST API fallback、stdout fallback)。

New%20session%20-%202026-06-03T11%3A32%3A04.293Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

可合并

本 PR 通过 prompt 指令 + escapeHashReferences 转义双层防御解决 GitHub 把 #N 自动渲染为 issue/PR 链接的问题。三个 reviewer 均无阻塞项。跨 reviewer 交叉验证后,3 条建议被 ≥2 名 reviewer 同时提出,标记为"已确认"。

阻塞项:无

建议项:

  • 【已确认】正则覆盖不全(性能无影响,但兜底不可靠):3 名 reviewer 指出 multi-review/src/platform.ts:155/(^|\s)#(\d{1,6})(?=[\s:.]|$)/gm 不覆盖 (#1)[#1]>#1:#1#1,、中文全角标点 :#1、#1,#1 等常见上下文。在中文评审中 阻塞项:#1 这类位置大概率漏转义。安全 reviewer 同步指出未来若支持中文标点需扩展 lookahead。建议放宽为 (?<=^|\s|\W)#\d+(?=\b) 或允许 ([{,)]} 等标点;否则一旦 LLM 偏离 prompt,平台层兜底失效,与架构 reviewer 提出的"平台层为权威"分层意图相矛盾。multi-review/src/platform.ts:155
  • 【已确认】指令文案 shotgun surgery 与各 action 出口未统一转义:质量 reviewer 与架构 reviewer 同时指出:architect-review/action.ymlfeature-missing/action.ymlreview/action.ymlspec-coverage/action.ymlmulti-review/reviewers/{quality,security,performance,architecture}.yamlmulti-review/src/orchestrator.tsmulti-review/src/reviewers.tsgithub-run-opencode/run-github-opencode.py 共 11 处复制了几乎相同的"Never use #N format..."指令;而确定性转义只加在 multi-review/src/platform.ts:postPRComment 一处。建议二选一:(a) 平台层兜底已足够,删除各 reviewer YAML / action.yml / orchestrator.ts 中的重复段,仅在 reviewers.tsrun-github-opencode.py 这类入口注入一次;(b) 提取共享 prompt 片段文件供各 action 引用。reviewers.ts:45-46run-github-opencode.py:461-466 的中英文文案还略有差异(标点/空格/措辞),需统一。
  • 【已确认】跨语言实现需同步机制:架构 reviewer 与质量 reviewer 共同提示:Python (run-github-opencode.py) 与 TS (reviewers.ts) 各自维护一份 ZH/EN 模板,后续调整易遗漏。建议在 README/AGENTS.md 留一条注记,或将文案抽到独立资源文件由两侧引用。
  • 【仅 quality】为 escapeHashReferences 补充单元测试:覆盖行首/行中/行尾、紧跟中英文标点(,(")、代码块内 #1 不应被改写、被零宽空格插入后 #1 不再被 GitHub 解析为引用等场景。
  • 【仅 quality】.gitignore 新增 __pycache__/ 与本 PR 主题无关:建议拆为独立 commit / PR,便于回溯。.gitignore:2
  • 【仅 quality】注释 // ── Escape #N references ──platform.ts:152)本身使用了 #N:源码注释虽不渲染到 PR,但与函数意图违和,建议改为 #<num> 等中性写法。
  • 【仅 architecture】reviewers.ts:45-49 拼接方式可读性下降:将语言指令与 hashAvoidZh/En 分两个变量再相加,建议直接在模板字符串里写完整段,或抽出 buildLangInstruction(language) 函数。
  • 【仅 architecture】prompt 层与平台层职责重叠:建议在 platform.ts / reviewers.ts 顶部加一行注释明确"平台层为权威保障,prompt 层仅为提示",避免后续维护者误判依赖关系。

📋 各 Reviewer 详细审查结果
quality

可合并
本 PR 通过"提示词指令 + 代码转义"双层防御解决 GitHub 把 review 文本中的 #N 自动转成 issue/PR 链接的问题。在 multi-review 里加了 escapeHashReferences 转义函数,其它 action 在 prompt 中加入了相同的中英双语说明。整体实现清晰,方向正确。

阻塞项:无

建议项:

  • multi-review/src/platform.ts:155 的正则 (^|\s)#(\d{1,6})(?=[\s:.]|$) 只覆盖行首或空白之后的 #N,对 (#1),#1>#1:#1 以及中文全角冒号 :#1(Chinese 评审中 阻塞项: 之后最可能的位置)等场景不会转义。建议放宽前置条件为 (?<=^|\s|\W)#\d+ 或直接全局替换所有 #\d+,否则当 LLM 未严格遵守 prompt 指令时,转义层在中文输出场景下会失效。multi-review/src/platform.ts:155
  • 转义函数只加在 multi-review 这条链路里(platform.ts),而 architect-reviewfeature-missingreviewspec-coverage 以及 github-run-opencode/run-github-opencode.py 都只加了 prompt 指令。如果设计意图是"所有 review 类 action 都应保证 #N 不会被 GitHub 改写",建议把转义函数抽到一个公共位置并在各 action 出口处统一调用,避免"prompt 服从性"作为唯一保障。
  • 建议为 escapeHashReferences 补充单元测试,覆盖以下场景:行首/行中/行尾、紧跟中英文标点(,(")、代码块内的 #1 不应被改写、被零宽空格插入后 #1 不再被 GitHub 解析为引用等。
  • .gitignore 新增 __pycache__/ 与本次 PR 主题无关(其它 action 与 multi-review 都不依赖 Python 源码目录),建议拆为独立 commit 或 PR,便于回溯。.gitignore:2
  • 注释 // ── Escape #N references ──…multi-review/src/platform.ts:152)本身使用了 #N 写法,虽然源码注释不会渲染到 PR,但与函数意图略显违和,可改为 #<num>#?N 等中性写法。
  • multi-review/src/reviewers.ts:45-46github-run-opencode/run-github-opencode.py:461-466 的中英文指令文案存在细微差异(标点、空格、措辞),建议统一为同一段模板,避免后续维护漂移。
security

安全无虞

本 PR 主要为评论输出增加 #N 转义处理(防止 GitHub 自动将 #1#2 渲染为 issue/PR 链接),并在不同 action 提示词中说明这一点。未涉及身份认证、密钥处理、输入校验逻辑或外部依赖变更。escapeHashReferences 使用零宽空格 &#8203; 包裹数字部分,属于纯展示层处理,未引入新的攻击面或注入风险。

阻塞项:无

建议项:

  • multi-review/src/platform.ts:151 的正则 (?=[\s:.]|$) 使用字符类,可考虑显式改为 (?=[\s:.]|$) 已正确;无需修改,仅作为对未来维护者的提示——若将来需要支持中文标点(如 、``,)等,请同步扩展该 lookahead,避免在中文语境下漏转义。
performance

性能良好

本 PR 的所有改动均不涉及性能敏感路径。核心新增逻辑仅在 multi-review/src/platform.ts 中引入了一个 escapeHashReferences 工具函数,配合 prompt 文本中追加少量说明字符串。

主要改动性能分析:

  • escapeHashReferences 使用单次正则替换(O(n)),正则模式 HASH_NUM_RE 在模块加载时编译一次(模块级常量),函数本身只对 PR 评论 body 整体做一次 pass,调用频率为每次评审一次,非热路径。
  • 正则 (\d{1,6}) 限定了量词上限,且使用非捕获预查 (?=[\s:.]|$),避免了回溯爆炸风险。
  • reviewers.tsrun-github-opencode.py 中的 hashAvoidZh/hashAvoidEn/zh_instruction/en_hash_instruction 均为模块级字符串常量拼接,每次 loadReviewers/_main 调用仅执行一次,零额外开销。
  • action.yml 和 reviewer *.yaml 内的提示文本仅作为 prompt 内容传给 LLM,不影响运行时性能。
  • .gitignore 追加 __pycache__/ 与性能无关,仅影响仓库体积。

潜在微观开销:

  • 每处匹配 #N 会插入 &#8203;(7 字节),对评论体积的影响可忽略(典型场景 < 100 字节)。

阻塞项:无
建议项:无

architecture

架构有疑虑

本 PR 通过两层防御解决 GitHub 自动将 #N 转为 issue/PR 引用的问题:在 prompt 层指示 LLM 避免输出 #N,并在 multi-review/src/platform.ts:postPRComment 入口处通过 escapeHashReferences 做确定性转义。后者位于评论投递边界,分层划分得当;前者在 11 个文件中复制了几乎相同的指令文本,存在散弹式修改隐患。

阻塞项:无

建议项:

  • 指令文本严重重复(shotgun surgery):同一段"Never use #N format ..."几乎逐字出现在 architect-review/action.ymlfeature-missing/action.ymlreview/action.ymlspec-coverage/action.ymlmulti-review/reviewers/{quality,security,performance,architecture}.yamlmulti-review/src/orchestrator.tsmulti-review/src/reviewers.tsgithub-run-opencode/run-github-opencode.py 共 11 处。仓库缺乏共享 prompt 片段机制,未来调整措辞需同步多个文件。可考虑:(a) 由于 platform.ts 的转义已经是确定性兜底,仅保留 reviewers.tsrun-github-opencode.py 这类集中入口注入即可,删除各 reviewer YAML / action.yml 的重复段;或 (b) 提取共享常量 / 片段文件,由各 action 在运行时拼接。
  • prompt 层与平台层的职责重叠escapeHashReferences 已能在出口处稳定修正所有 #N,prompt 层指令在正确性上其实是冗余的。当前"双保险"在架构上可接受,但应明确"平台层为权威保障,prompt 层仅为提示"的语义,避免未来误以为某一侧失效就会出问题。
  • reviewers.ts:45-49 拼接方式可读性下降:将语言指令与 hashAvoidZh/En 分两个变量再相加,阅读时需跳读多处。考虑直接在模板字符串里写完整段,或抽出一个 buildLangInstruction(language) 函数,与未来可能继续追加的指令归一管理。
  • platform.ts:153 转义正则覆盖不全/(^|\s)#(\d{1,6})(?=[\s:.]|$)/gm 不会匹配 (#123)[#123]#123, 等常见上下文。作为最后兜底层,建议放宽前后界(如允许常见标点 ([{,)]} 等),否则一旦 LLM 在这些上下文中产出 #N,平台层就失去防御能力,重新依赖 prompt 层指令——这违背了"平台层为权威"的分层意图。
  • Python 与 TS 两套实现需保持同步run-github-opencode.pyreviewers.ts 各自维护了一份 ZH/EN 文案;后续若调整任一侧,容易遗漏另一侧。可在 README/AGENTS.md 留一条注记,或将文案抽到独立资源文件被两侧引用。

@Svtter Svtter merged commit 5437de3 into main Jun 3, 2026
3 checks passed
@Svtter Svtter deleted the fix/hash-reference-issue-173 branch June 3, 2026 12:19
Svtter added a commit that referenced this pull request Jun 3, 2026
- Widen escapeHashReferences regex to cover more contexts: parentheses,
  brackets, angle brackets, colons, and Chinese punctuation (:,、)
- Skip code blocks (triple backtick fences) during escaping
- Insert zero-width space between # and digits (not after) for correct
  GitHub reference breaking
- Extract buildLangInstruction() in reviewers.ts for readability
- Add architectural comment clarifying platform layer is authoritative
- Use neutral notation in source comments (#<num> -> hash-number)
- Unify Chinese/English instruction text between Python and TS
- Add 14 unit tests for escapeHashReferences covering line start, after
  punctuation, Chinese punctuation, code block exclusion, and headings

Addresses suggestions from PR #175 multi-review.
@github-actions github-actions Bot mentioned this pull request Jun 4, 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.

fix: agent 使用 #N 格式会被 GitHub 自动转换为 issue/PR 引用

1 participant