Skip to content

fix(review): resolve fork pull-request runs in review snapshot - #564

Closed
tietoumiao wants to merge 1 commit into
T-Auto:mainfrom
tietoumiao:fix/495-review-fork-pr-run
Closed

tietoumiao wants to merge 1 commit into
T-Auto:mainfrom
tietoumiao:fix/495-review-fork-pr-run

Conversation

@tietoumiao

@tietoumiao tietoumiao commented Nov 1, 2026 •

Copy link
Copy Markdown

Closes #495

What was wrong

src/review/snapshot.ts:47-48 treated the workflow run's pull_requests array as a required join
key. For a pull_request run triggered from a fork, GitHub returns pull_requests: [] on the run
object even though the run, its conclusion and the PR's mergeable fact all exist. The snapshot
therefore reported the fork PR's CI/merge state as unknown, which is the state reviewers are told
to treat as "evidence missing".

Reproduced with the snapshot fixtures on Windows 11 x64 at main 065cd7f; the fork case was absent
from test/review-snapshot.test.mjs even though the suite title claims fork/claim coverage.

The change

  • src/review/snapshot.ts — when pull_requests is empty, link the run through the facts the run
    actually carries (head_repository + head_branch) and record which association was used
    (ci.association: "pull_requests" | "head"). The GraphQL query now also asks for mergeable /
    mergeStateStatus and ships them as githubMerge, so merge evidence is only "missing" when both
    githubMerge and the local probe are unresolved.
  • test/review-snapshot.test.mjs — fork coverage: a run with pull_requests: [] resolves to its
    real state; an action_required fork run surfaces action_required instead of unknown; a run
    that cannot be corroborated still fails closed to unknown.
  • docs/review-snapshot.md + docs/审阅快照.md + docs/CHANGELOG.md.

Deliberately not done: no change to how the local probe works, and no guessing from
head_branch alone — an unverifiable association still yields unknown (fail-closed, never a false
green).

Validation

$ node test/run.mjs review-snapshot
  ok   fork pull-request runs without a PR link resolve from verified head fields
  ok   fork runs awaiting maintainer approval surface action_required instead of unknown
  ok   missing runs, bounded evidence and API failures stay partial or unknown, never green
dsh-ops review-snapshot: all 6 checks passed

$ node test/run.mjs review-snapshot      # after reverting only src/review/snapshot.ts
dsh-ops review-snapshot: 4 of 6 check(s) failed
  (fork association undefined; '+ partial' vs '- completed'; unverifiable-association guard red)
  • 撤掉本次实现后变红的检查:review-snapshot 的 fork 三条 + 未验证关联守卫
  • docs/CHANGELOG.md ## [Unreleased]:已加(EN+ZH)
  • 面向模型的描述 / 参数:无变化(这是宿主侧审阅工具的数据,不进模型 schema)
  • CI:待维护者批准后由 pull_request 事件触发,届时回填 CI required 的 run URL

Environment

  • OS / 架构:Windows 11 x64
  • Node:24.16.0
  • 基于上游 main 的 commit:065cd7f
  • 安装通道:源码 tarball + npm ci --ignore-scripts

未验证 / 风险披露

  • 没有真实 GitHub 响应:head_repository.full_name / head_branch 的存在性取自 issue 的现场
    (run 40901867835),以及 GraphQL 对 headRefName/mergeable/mergeStateStatus 的接受度。
    若某个字段缺失或对不上,结果是 unknown(fail-closed),不会误报绿。
  • 全量套件在本机有一次 4/167 的红色集合,与 pristine main 同口径一致(受限 bash 抖动)。

CI

$ gh run view 41338668509 --json url,event,conclusion --jq '"(.url) (.event) (.conclusion)"'
https://github.com/T-Auto/dsh-ops/actions/runs/41338668509 pull_request action_required

A pull_request run from a fork reports no pull_requests link, so the exact head-SHA match was dropped and the PR showed unknown CI. Accept such a run only when its head repository and head branch match the PR head, keep runs linked to another PR out, and expose GitHub mergeable/mergeStateStatus as githubMerge so merge evidence does not need local commit objects.

@Haoming9527 Haoming9527 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fork-run association work addresses the same #495 path as #538, which is already included in the reviewed integration #557. Please compare the two alternatives before merging both.

One correctness issue remains in the merge-evidence fallback: GitHub's mergeable describes the PR's actual base, while this command accepts a different requested --base. The new partial-status condition treats any known GitHub mergeability as sufficient, without requiring the two base identities to match.

Reproduced against head 566b6d15 with an injected API runner: requested main SHA 111…, PR base release SHA 444…, GitHub MERGEABLE/CLEAN, and no local checkout. This implementation returns status: completed even though its local probe is unknown and no merge evidence covers the requested base. The #538 implementation returns partial and records githubMerge.matchesRequestedBase: false for the same response.

Please carry the GitHub assessment's base ref/SHA and only use it to satisfy the requested-base evidence when both identities match. Add a mismatched-base regression alongside the new fork cases. This finding does not assert that a conflict exists; it prevents reporting an unrelated base's merge assessment as complete evidence.

@Nicholas023 Nicholas023 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查结论:REQUEST_CHANGES — 建议关闭

fork CI 关联这部分是有效的,但 merge 证据有一个会让审查者误判的缺陷。

1. mergeable 评估的是 PR 的真实 base,而不是请求的 --base(阻断)

用真实 GitHub 数据(issue #495 自己的复现对象 PR #487):

text $ node bin/dsh-ops.mjs review snapshot --repo T-Auto/dsh-ops --prs 487 --base main "status":"completed" "prBaseRef":"master" ← PR 实际基于 master "githubMerge":{"mergeable":false,"state":"DIRTY"} ← 这是对 master 的评估 "merge":{"status":"unknown","reason":"No --checkout supplied; no local merge probe."}

请求的是 main 的证据,拿到的是 master 的合并评估,却报 completed,而 githubMerge 里没有任何 base 身份字段可供核对。

根因在 partial 判定:
ts || (pr.merge.status === 'unknown' && pr.githubMerge.mergeable === null))
只判断 GitHub 是否「知道」合并状态,从不比较 GitHub 评估的 base 与 --base 是否同一 SHA。#538 的实现返回 partial 并记录 matchesRequestedBase:false。

2. 删除了「用户显式要求本地探针」的 partial 条件

基线在 --checkout 探测失败时返回 partial,本 PR 返回 completed。这是未在 body 中声明的行为变化。

已核实为真的部分

  • 0.5.0 基线对该 fork PR 确实报 "ci":{"status":"unknown","reason":"No matching run in the latest 20 results; absence is not a pass."}(问题真实存在)
  • node test/run.mjs review-snapshot → 6/6 passed
  • revert-red(只撤 src/review/snapshot.ts)→ 4/6 failed,与声明一致
  • 与 #538 的 5 个重叠文件全部冲突

处置建议

关闭。 由 #538 承接。若想保留「空关联时再用 head 仓库+分支佐证」这一更严格守卫,应在 #538 上做小补丁,而不是保留本 PR。

@T-Auto T-Auto closed this Nov 2, 2026
@tietoumiao

Copy link
Copy Markdown
Author

Closing — #538 now owns the review-snapshot change and is in main (#557). The two defects the review named (mergeability evaluated against the PR's real base rather than --base, and the silently dropped partial condition) are exactly why this branch should not be carried forward as-is; the empty-pull_requests guard is worth folding into #538 only if the base-identity comparison lands first.

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.

[BUG] review snapshot:fork PR 的 CI/merge 被误报 unknown(run 与 mergeable 实际存在)

4 participants