feat(web-shell): derive session issue bindings from the closing references of bound PRs - #10425
feat(web-shell): derive session issue bindings from the closing references of bound PRs#10425wenshao wants to merge 10 commits into
Conversation
…ences of bound PRs Sessions could be found by the PR they produced, but not by the issue they were working on — the maintainer flow starts from an issue and ends in a PR. GitHub already knows that link: the PR body's "Fixes #N" is its closing reference. Snapshot those references, with the issue state, onto each bound PR entry in the existing sidecar instead of adding a client-side binding, a second sidecar, or a prompt/branch heuristic that would misbind across the shared PR/issue number space. The refresh sweep gains a second, by-number GraphQL lookup: every non-merged binding (closing references change while a PR is open) and every merged binding that predates the snapshot (one catch-up) is queried in batches, and the result is written in place together with the PR state — order and createdAt untouched, url-mismatched (foreign repository) entries never touched, unchanged sidecars never rewritten. The slim PR list query stays as it was: nesting closing references under it measurably slows it and still carries no issue state, while a by-number lookup also reaches PRs outside the 500-entry window. gh exits non-zero over a NOT_FOUND alias yet still prints the other aliases, so the wrapper parses that payload rather than failing the batch. Every projection from the sidecar to the wire now goes through one shared helper, the bridge keeps the daemon-derived snapshot across a client re-bind, and the SDK guard validates issue entries with the same url rules as PRs. The Web Shell tooltip lists the issues under the PR rows (deduped across stacked PRs) with GitHub-style state icons, and sidebar search matches an issue number with or without the hash.
|
Thanks for the PR! Template looks good ✓ Problem: a real, observed workflow gap rather than theoretical hardening — the maintainer flow (bugfix / triage / autofix develop-issue) starts from an issue, and after #9729 and #10258 the sidebar could answer "which session produced PR #N" but not "which session is working on issue #N". Deriving issues from GitHub's own closing references is the high-precision source, since the Web Shell has no structured issue-binding write point. Direction: aligned. This is the natural next step of the session-binding feature line established by the two merged PRs it builds on. Claude Code's CHANGELOG has no direct counterpart, but the area is clearly established in this repo. No auth/sandbox/model-selection/telemetry surface is touched; the SDK type change is an additive optional field. Size: cross-package change spanning core, cli serve, acp-bridge, SDK, and web-shell. 1810 changed lines total: 784 production logic, 839 test, 187 docs/e2e-plan. The author is a maintainer, so the two-tier core gate does not apply — reported for information only. Approach: the scope feels right. Every edit serves the stated goal; the new shared Risk: no elevated risk signals — none of the changed files match the revert-correlated high-risk paths. One heads-up on the duplicate check: GitHub's closing-reference parser links this PR to #10270, which is closed as completed (by merged #10278). That link is accidental prose — the Evidence section describes test data as "(#10278, which closed #10270)" — and #10270 (modality auto-detection) is unrelated to this change, whose Linked Issues section correctly says "Builds on #9729 / #10258". No action taken on it. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题: 真实的工作流缺口,而非理论性加固——维护者的主力流程(bugfix / triage / autofix develop-issue)从 issue 出发,而 #9729、#10258 落地后侧栏只能回答"哪个会话产出了 PR #N",回答不了"哪个会话在处理 issue #N"。Web Shell 没有结构化的 issue 绑定写入点,从 GitHub 自己的 closing references 派生是唯一高精度来源。 方向: 对齐。这是 #9729、#10258 两个已合入 PR 确立的会话绑定功能线的自然延伸。Claude Code CHANGELOG 无直接对应,但该方向在本仓库已明确确立。不涉及 auth/沙箱/模型选择/遥测;SDK 类型变更为可选字段的增量添加。 规模: 跨包改动,涉及 core、cli serve、acp-bridge、SDK、web-shell。共 1810 行:784 行生产逻辑、839 行测试、187 行文档/e2e 计划。作者是维护者,两级核心门禁不适用——仅记录备查。 方案: 范围合理。每处改动都服务于既定目标;新增的共享投影 风险: 无升级风险信号——改动文件均未命中与 revert 相关的高风险路径。 重复检查提示: GitHub 的 closing-reference 解析器把本 PR 关联到了 #10270(已作为 completed 关闭,由已合入的 #10278 关闭)。该关联是行文误触——Evidence 一节描述测试数据时写了 "(#10278, which closed #10270)"——#10270(模态自动检测)与本改动无关,Linked Issues 一节也正确写明"基于 #9729 / #10258"。未据此采取任何动作。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRead against What I verified rather than took on faith:
Two observations, neither blocking: the SDK guard accepts issue lists of any length while the sidecar caps at 10 — fine, since the cap is a storage policy and the daemon only ever produces ≤10 ( Files changed (29 of 29)
TestingThis is an unattended CI run, so nothing was built or executed here — the evidence below is the PR's own CI as seen through the API, quoted verbatim.
To be plain about the gap: as of this review the PR's own CI ( The test results quoted in the PR body (per-package unit counts, and a real-daemon sweep against this repository with the real Sandboxed verification would settle what neither static review nor (currently absent) CI can: 中文说明代码审查基于 我逐项核实而非照单全收:
两点非阻塞观察:SDK 守卫不限 issue 列表长度而 sidecar 上限 10——无碍,上限是存储策略且 daemon 只产出 ≤10( 测试本次为无人值守 CI 运行,未构建、未执行任何代码——以上证据是通过 API 读取的 PR 自身 CI,原样引用。 明确说明缺口:截至本次审查,PR 自己的 CI( PR 正文引用的测试结果(各包单测数量、macOS 上用真实 沙箱验证可以补上静态审查与(目前缺席的)CI 都补不了的缺口: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean review across every stage; the one thing standing between this and a merge is CI evidence, which does not exist yet for this commit. Stepping back: the approach matches what I'd have proposed independently, and exceeds it on the edges — the NOT_FOUND handling, the legacy catch-up that costs one call and then zero, and the url guard surviving the merge of two queries are exactly the places where a lazier version of this feature would have leaked. The problem is real (the maintainer flow starts at an issue; the sidebar could find sessions by PR but not by issue), the direction is a continuation of two already-merged PRs, and every line in the diff earns its place — the projection consolidation included, since the new field would otherwise have touched those 8 sites individually. If I were maintaining this in six months, the three-layer validation and the The honest reservation: I could not verify a single runtime claim. The PR's own CI has not produced any run on this commit as of this review (detailed in the Stage 2 comment), and this run executes nothing by rule. The author's reported results look plausible and the mocked tests are thorough, but plausible is not evidence. That is what the deferred approval below is for — and the sandboxed lanes named in Stage 2 ( Approval is deferred until the PR's own CI lands green on this commit; the finalize step will post the commit-pinned approval then. 中文说明置信度:4/5 —— 各阶段审查均干净;挡在合入前的唯一事项是 CI 证据,而该 commit 目前还没有任何 CI 记录。 退一步看:方案与我独立设想的一致,且在边界处理上更胜一筹——NOT_FOUND 处理、存量补齐只花一次调用随后零调用、两条查询合并后 url 守卫仍然有效,正是这类功能偷懒实现会漏的地方。问题是真实的(维护者流程从 issue 出发,侧栏能按 PR 找会话却不能按 issue 找),方向是两个已合入 PR 的延续,diff 中每一行都必要——投影统一也包括在内,否则新字段要逐一改到那 8 处。若六个月后由我维护,三层校验与 诚实的保留意见:我无法核实任何运行时结论。截至本次审查,PR 自己的 CI 在该 commit 上没有任何运行记录(详见 Stage 2 评论),且本次运行按规则不执行任何代码。作者自述的结果看起来合理、mock 测试也周全,但合理不等于证据。这正是下方延迟审批的用途——Stage 2 提到的沙箱通道( 审批延迟到 PR 自己的 CI 在该 commit 上全绿之后;届时 finalize 步骤会发布绑定该 commit 的审批。 — Qwen Code · qwen3.8-max Reviewed at |
|
@qwen-code /resolve |
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
Test Plan (not a blocker): 78 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 102 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 1101 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 805 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 115 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; and 1 more.
中文说明
已审查——无阻断问题。 建议见行内评论。
Test Plan(非阻断):78 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 102 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 1101 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 805 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; 115 passed — this review observed 1795, 21984, 1668, 4335, 605, 639 passed; and 1 more。
— qwen3.8-max via Qwen Code /review (v0.22.2)
|
Qwen Code attempted to resolve merge conflicts but the run did not complete successfully. Check the workflow run for full logs. |
|
🤖 AutoFix ran out of time before finishing (timeout (3600000ms)) (attempt 1/100) — it will retry on the next scan.
See the Qwen Autofix agent step logs for model/tool output. 中文说明🤖 AutoFix 在完成前耗尽了时间(timeout (3600000ms))(第 1/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33211695484 🧠 Handled by Qwen Code · model/模型 |
…carry on the PR's url Review round 1 on the issue-snapshot sweep. A merged binding the repository cannot resolve — another repository's same-numbered PR — could never receive a snapshot, so it re-entered the by-number lookup on every sweep and broke the "all merged with snapshots costs no call" invariant; the sweep now writes it a converging empty snapshot once the lookup has succeeded, and only ever stores issues the lookup actually resolved for that url. The session-list merge of persisted and live bindings compared numbers alone, the one carry site that did not check the canonical url, so a cross-repository re-bind could briefly wear the previous repository's issues; it now applies the same url gate as the sidecar and bridge. The closing-references fetch bound and the sidecar's per-PR cap were two literals tied by a comment (raising one would void every sidecar on read); they are one constant now, declared in the utils layer the sidecar service imports. The GraphQL wrapper passes its own timeout to the error formatter, and the write-side binding types on the bridge and the SDK omit issues so a client-bound issue list is a compile error, matching the runtime drop. Tests pin each of these plus the gaps the review found: the killed-timeout message, the list-query failure still snapshotting issues, client-supplied issues dropped by the bridge, the cross-repository re-bind dropping the snapshot, a closed issue with no state reason mapping to completed, the open issue-state rendering, the full `-f query=` argument, and the SDK guard's number and non-object checks.
🖼️ web-shell visual previewRendered against a mock daemon (no real backend): the PR base vs this PR head Screenshots · before / afterℹ️ No screenshot changed against the PR base — but this PR edits 4 render-shaping files:
Either the change has no visual effect (logic, plumbing, a state the scenarios never reach), or no scenario renders this UI — in which case the preview cannot see it, and an empty result is a coverage gap rather than a clean bill of health. To make it visible, add a scenario to Full-resolution recordings (.webm) are attached to the workflow run. — Qwen Code · web-shell visuals |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
🩺 serve daemon A/BBuilt the PR base vs this PR head ✅ No response changes against the PR base across 12 scenario(s). — Qwen Code · serve A/B |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Test Plan (not a blocker): 78 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 102 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 1101 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 805 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 115 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; and 1 more.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/core/src/utils/github-pr-issues.ts:120 — [probe] query-level GraphQL failure surfaces a generic message, discarding gh's error textpackages/cli/src/serve/routes/session-pr-backfill.ts:719 — [probe] issues forwarding through backfill/bind projections pinned by no testpackages/cli/src/serve/server/session-list.ts:570 — [probe] mergeSummaryPrs issues overwrite pinned only for fill-in, not stale overwritepackages/core/src/utils/github-pr-issues.test.ts:206 — [probe] gitEnv denylist scrub at the new gh call site pinned by no test
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
Test Plan(非阻断):78 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 102 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 1101 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 805 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; 115 passed — this review observed 1796, 25901, 22043, 1691, 4446, 605, 639 passed; and 1 more。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
🤖 AutoFix ran out of time before finishing (timeout (3600000ms)) (attempt 2/100) — it will retry on the next scan.
See the Qwen Autofix agent step logs for model/tool output. 中文说明🤖 AutoFix 在完成前耗尽了时间(timeout (3600000ms))(第 2/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33233589564 🧠 Handled by Qwen Code · model/模型 |
|
🤖 AutoFix hit a verification-gate error before reaching a verdict (attempt 3/100) — it will retry on the next scan. Round summary — PR #10425 (address-review, round 3)Two prior rounds exhausted the agent time budget, so this round implemented Base-conflict resolution (--conflict true)Merged Feedback points and dispositions
中文说明🤖 AutoFix 在得出结论之前遇到验证门错误(第 3/100 次尝试)—— 将在下次扫描时重试。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33239705848 🧠 Handled by Qwen Code · model/模型 |
…ect non-safe PR numbers Review round 2 on the issue-snapshot sweep. A merged binding no lookup can ever snapshot now converges in every case where that is knowable — the lookup succeeded without resolving it, the lookup is structurally impossible (no gh binary, no git root), or the platform has no closing references at all (Aone) — while a transient failure still retries, since it says nothing about the PR's references. A non-merged binding to another repository stays out of the lookup altogether: the list query names this workspace's repository, and a binding outside that canonical prefix can never resolve here, the GitHub twin of the Aone refreshability filter. A workspace with only unresolvable bindings therefore costs nothing after one sweep, and a mixed workspace still refreshes its open bindings' state. The GraphQL wrapper accepts safe integers only: a positive integer-valued double at or beyond 1e21 stringifies in exponential notation, an invalid Int literal that failed the whole document instead of one alias — one such binding would have disabled issue refresh for the entire workspace. The session-list merge finds the persisted entry by url rather than a last-wins number map, so a hand-edited sidecar with two same-numbered entries cannot shadow the live binding's own snapshot. Tests cover each path (lookup resolving nothing, foreign open binding never queried, gh unavailable converging, transient failure retrying, mixed workspace running the list query, generation closing between the two queries, merged-only Aone workspace going quiet, the repository scope in the query, and the safe-integer bound), and the E2E plan's bridge filter now selects the client-supplied-issues test.
|
🤖 Could not produce a passing fix for this feedback (round 4/100) — the verification gate rejected the attempt. This item now needs a human; the loop stays engaged and still picks up new feedback and base conflicts, but will not retry this item on its own. Review round summary — PR #10425Same-run verification repair (the deterministic rejection)The previous commit was rejected because the gate's
Base-conflict resolutionMerged Findings addresseWhy it was not pushed: tests failed in packages/cli 中文说明🤖 未能为该反馈产生可通过验证的修复(第 4/100 轮) —— 验证门拒绝了该尝试。此项现在需要人工处理;循环保持在线,仍会拾取新反馈与 base 冲突,但不会自行重试此项。 验证门的拒绝原因与日志证据见上方英文部分(gate-rejection 不翻译)。 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33245186912 🧠 Handled by Qwen Code · model/模型 |
wenshao
left a comment
There was a problem hiding this comment.
Test Plan (not a blocker): 78 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 102 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 1101 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 805 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 115 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; and 1 more.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/server/session-pr-refresh.ts:356 — [review] second assertGenerationOpen() before the issue fetch is unwitnessed — deleting the assert keeps the suite green (both generation tests close the guard before/after this poin…packages/sdk-typescript/src/daemon/session-pr.ts:68 — [review] SDK runtime guard accepts unbounded issues arrays, diverging from the core sidecar validator's 10-cap the design doc claims synchronized
中文说明
Test Plan(非阻断):78 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 102 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 1101 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 805 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; 115 passed — this review observed 1796, 25884, 22138, 1691, 4446, 610, 639 passed; and 1 more。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
— glm-5.3 via Qwen Code /review (v0.22.2)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — no test suite executed (per-call budget exhausted on clean builds; packages/web-shell never compiled); Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not explored to full depth (tool budget reached): chunk 1: executing the new/changed bridge tests — the review worktree (and the parent checkout) has no node_modules, and npm ci plus the workspace build the vitest glo….
Not reviewed: reverse audit — stopped before round 4 by the review time budget.
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
packages/web-shell/client/components/sidebar/SessionDetailsTooltip.test.tsx:259 — [probe] tooltip issue-dedup test never pins the newest-wins keep-direction (both duplicates of issue #7 carry identical state)packages/core/src/utils/github-pr-issues.test.ts:314 — [probe] chunk test's -3/2.5 exclusion unobservable — the mock responder regex cannot match a sign or a dotpackages/acp-bridge/src/bridge.ts:10449 — [probe] setSessionPrs issues pass-through pinned by no test — the 806-test bridge suite stays green under a full revertpackages/core/src/utils/github-pr-issues.test.ts:98 — [probe] mapIssue guard clauses 2-4 (number <= 0, non-integer number, missing url) pinned by no test
Convergence: round 3 posted 7 inline comment(s), 7 of them reported for the first time; the previous round posted 11 (11 new). Findings keep coming back to the same files: packages/core/src/utils/github-pr-issues.ts (findings in round 2; 2 more now); packages/cli/src/serve/server/session-pr-refresh.ts (findings in round 2; 1 more now); packages/core/src/utils/github-pr-issues.test.ts (findings in round 1; 1 more now). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — no test suite executed (per-call budget exhausted on clean builds; packages/web-shell never compiled); Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未探索到全部深度(达到工具调用预算):chunk 1:executing the new/changed bridge tests — the review worktree (and the parent checkout) has no node_modules, and npm ci plus the workspace build the vitest glo…。
未审查:反向审计——评审时间预算不足,未能开始第 4 轮。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 3 轮发布了 7 条行内评论,其中 7 条是首次提出;上一轮发布了 11 条(其中 11 条首次提出)。发现反复回到同一批文件:packages/core/src/utils/github-pr-issues.ts(第 2 轮已出过发现,本轮又有 2 条);packages/cli/src/serve/server/session-pr-refresh.ts(第 2 轮已出过发现,本轮又有 1 条);packages/core/src/utils/github-pr-issues.test.ts(第 1 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
🤖 AutoFix stopped after 5 consecutive rounds that pushed nothing (failed rounds, timeouts, gate rejections, or stops under instruction). Retrying at the same per-round budget is not converging — this usually means the PR is too large or conflicts with a fast-moving Round summary — Critical fixed, unwitnessed guards pinned, prior gate rejection diagnosedThis round landed the smallest blocking subset after two consecutive rounds exhausted Feedback dispositionsFixed (resolved in the code, commit ab46a81850):
中文说明🤖 AutoFix 已停止:连续 5 轮未能推送任何内容(失败轮次、超时、验证门拒绝或按指示停止)。以相同的单轮预算重试并不收敛 —— 这通常意味着 PR 过大,或与快速变动的 Run log: https://github.com/QwenLM/qwen-code/actions/runs/33265601025 🧠 Handled by Qwen Code · model/模型 |
|
⏸️ Takeover paused: this PR reached its round cap (100/100). Comment 中文说明⏸️ 托管已暂停:本 PR 达到轮次上限(100/100)。评论 |
…PR identity Review round 3. The response parser read only the repository data and dropped the top-level GraphQL errors, so a server error nulling one alias, or a sub-field error nulling a resolved PR's closing references, came back as a successful lookup — and the sweep then retired a merged binding on that "absence" with a permanent empty snapshot. Only an alias-level NOT_FOUND now counts as absence; every other partial error fails the call, which keeps retrying. A repository gh cannot resolve at all (no git remotes, or none on a GitHub host) is a structural failure, not a transient one, and converges like a missing gh binary; execFile's error carries no stderr of its own, so the wrapper attaches it before classifying. Lookup results are matched to bindings by host, owner, repository, and number rather than by canonical url, so a binding spelled with `www.`, `http:`, or a `/files` suffix receives the issues fetched for it — written under the binding's own url, which the sidecar's canonical write gate requires — instead of being mistaken for a foreign repository and retired empty. The SDK guard caps issue lists at the sidecar's ten, matching the design. Tests: partial-error rejection with the NOT_FOUND tolerance kept, the repository-unresolved kind, the variant-url binding keeping its fetched issues, convergence under not_a_repo and repo_unresolved, a foreign merged binding converging during a lookup outage, the Aone workspace never reaching the GitHub lookup and never re-resolving its origin once quiet, the canonical-equal url spelling inheriting the sidecar snapshot in the session list, the sanitized env dropping GH_REPO, the exact safe-integer literal, and a state-less issue accepted by the SDK guard.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed. Suggestions are inline.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R4-13 backfill live-entry sync issues projection unpinned — already reported (round-2 deferred list, review 5056802503, session-pr-backfill.ts:719)
Not reviewed: build-and-test — no test suite executed: npm ci green, 13 of 19 workspaces built green (packages/core compiled the PR's changed sources), packages/sdk-typescript build timed out against the per-call infrastructure budget before any suite ran; Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally; test-efficacy probe inconclusive (harnessValidated: null).
Not reviewed: reverse audit — reached the 5-round cap without converging (every round reported findings).
Not explored to full depth (tool budget reached): chunk 6: test execution of packages/core/src/utils/github-pr-issues.test.ts — attempted, but the worktree has no node_modules and a full install+build was not viable h…; "agent reverse-audit (round 5)": none — the walk completed within budget..
Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:
packages/sdk-typescript/test/unit/sessionPr.test.ts:51 — [probe] hasControlCharacter clause unpinned — mutant deleting it survives the suite (deferred under the code-age rule: anchored on code unchanged since round 3)
Convergence: round 4 posted 10 inline comment(s), 10 of them reported for the first time; the previous round posted 7 (7 new). Findings keep coming back to the same files: packages/core/src/utils/github-pr-issues.test.ts (findings in round 3; 1 more now); packages/core/src/utils/github-pr-issues.ts (findings in round 3; 1 more now). The rate of new findings is not falling. A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. Batching the remaining fixes and verifying them before the next push, or dropping this PR's reviews to --severity-floor critical, keeps the loop from re-deriving the same set. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — no test suite executed: npm ci green, 13 of 19 workspaces built green (packages/core compiled the PR's changed sources), packages/sdk-typescript build timed out against the per-call infrastructure budget before any suite ran; Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally; test-efficacy probe inconclusive (harnessValidated: null)。
未审查:reverse audit — reached the 5-round cap without converging (every round reported findings)。
未探索到全部深度(达到工具调用预算):chunk 6:test execution of packages/core/src/utils/github-pr-issues.test.ts — attempted, but the worktree has no node_modules and a full install+build was not viable h…;"agent reverse-audit (round 5)":none — the walk completed within budget.。
收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 4 轮发布了 10 条行内评论,其中 10 条是首次提出;上一轮发布了 7 条(其中 7 条首次提出)。发现反复回到同一批文件:packages/core/src/utils/github-pr-issues.test.ts(第 3 轮已出过发现,本轮又有 1 条);packages/core/src/utils/github-pr-issues.ts(第 3 轮已出过发现,本轮又有 1 条)。新发现的产出速度没有下降。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。把剩余修复攒成一批、验证后再推送,或将本 PR 的评审降到 --severity-floor critical,可以避免循环反复推导同一组发现。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.3)
… unresolved Review round 4. A renamed, deleted, or access-revoked repository answers the lookup with a NOT_FOUND on the repository itself rather than on an alias; that was thrown as a partial error and reported as a transient failure, so merged bindings in such a workspace never converged and the sweep kept paying the failing lookup. It now classifies as the same structural kind as a missing GitHub remote, which the sweep already retires bindings on. Test strengthening the round asked for: one fixture per accepted gh diagnostic, the repository-level NOT_FOUND payload, the SDK guard accepting exactly ten issues and rejecting a control character in an issue url, and the tooltip fixture reworked so binding order contradicts number order, a filtered PR's issues disappear with it, a same-numbered issue from another repository stays distinct, the merged PR contributes an observable issue, non-openable issues leave no text behind, and the state-less icon check reads the class token list instead of jsdom's SVGAnimatedString.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — the build-test harness ran no test suite (the build phase exhausted the per-call budget before the test phase; the packages/web-shell lib build timed out against the infrastructure budget); Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not reviewed: reverse audit — reached the 5-round cap without converging (rounds 1, 3, 4 and 5 reported findings; chunks 9 and 11 retired dry in rounds 3-4).
Not explored to full depth (tool budget reached): "agent 1b": none — no check was cut short..
Deferred under the convergence posture (round 5, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/acp-http/dispatch.ts:3095 (+5 locations) — [probe] R5-5 issues projection unpinned at all five wire sites — deferred under the code-age rule (anchored on code unchanged since round 4); the backfill site was previously…packages/cli/src/serve/server/session-list.ts:570 — [probe] R5-6 sidecar-beats-live issues merge direction untested — deferred under the code-age rule (anchored on code unchanged since round 4)
Convergence: round 5 posted 3 inline comment(s), 3 of them reported for the first time; the previous round posted 10 (10 new). Findings keep coming back to the same files: packages/core/src/utils/github-pr-issues.test.ts (findings in round 4; 1 more now). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had no anchor this round could use either — none at all, one with no certifier, one certified by an identity other than the one this round runs under, or one this round's fetch refused or resolved to the head — so the next review re-reads the whole diff unless recovery grafts an earlier own anchor that the round running it can use onto the complete work list this round leaves behind, and keeps doing so until a round's marker carries an anchor again or a graft lands that the round running it can use. (Stated, not acted on — this changes nothing about what the round posts.)
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — the build-test harness ran no test suite (the build phase exhausted the per-call budget before the test phase; the packages/web-shell lib build timed out against the infrastructure budget); Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未审查:reverse audit — reached the 5-round cap without converging (rounds 1, 3, 4 and 5 reported findings; chunks 9 and 11 retired dry in rounds 3-4)。
未探索到全部深度(达到工具调用预算):"agent 1b":none — no check was cut short.。
收敛姿态下延后(第 5 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 5 轮发布了 3 条行内评论,其中 3 条是首次提出;上一轮发布了 10 条(其中 10 条首次提出)。发现反复回到同一批文件:packages/core/src/utils/github-pr-issues.test.ts(第 4 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有留下本轮可用的锚点——要么完全没有、要么没有认证者、要么由本轮运行身份之外的身份认证、要么被本轮的获取拒绝或解析为头提交——因此下一次评审将重读整个 diff,除非恢复流程把本轮能使用的更早自有锚点嫁接到本轮留下的完整工作清单上;并会一直如此,直到某一轮的标记重新带上锚点,或落地的嫁接能被运行该轮的评审使用。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.3)
…PR url spelling Review round 5. gh's "error parsing owner value" prefix also fronts its GH_HOST-mismatch diagnostic — an environment problem, not a missing repository — so the structural classification is narrowed to the two wordings that really mean no usable GitHub remote, and the mismatch stays a transient failure that never retires bindings. PR identity now also recognizes the `.diff` and `.patch` spellings GitHub serves, so such a binding receives its fetched issues instead of a permanent empty snapshot. And the write under a binding's own url no longer waits for a successful issue lookup: during an outage the list query's state still lands on a non-canonically spelled binding instead of being dropped by the sidecar's canonical gate. Tests: the GH_HOST diagnostic stays `failed`, the three structural wordings converge, `.diff` and `.patch` bindings keep their fetched issues alongside the `/files` spelling, and a `www.`/`/files`-spelled open binding turns merged while the lookup is failing.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R5-5 issues projection unpinned at all five wire sites — already reported (round-5 deferred list, review 5061353666)
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally; the packages/cli and packages/core unit suites also hit the harness infrastructure deadline on this contended host before completing (every failing file they stamped is unchanged in this PR).
Test Plan (not a blocker): 78 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 102 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 1101 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 805 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 115 passed — this review observed 1820, 1722, 5189, 481, 94 passed; and 1 more.
Deferred under the convergence posture (round 6, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/server/session-pr-refresh.ts:73 — [probe] pullRequestKey regex case-sensitive on /pull/ and .diff/.patch — uppercase-spelled merged bindings converge to a wrong permanent issues: []packages/core/src/utils/github-pr-issues.ts:58 — [probe] repo_unresolved pattern also matches an unauthenticated-GHE diagnostic — a recoverable state converges merged bindings to permanent issues: []packages/sdk-typescript/src/daemon/session-pr.ts:13 — [probe] SDK MAX_SESSION_PR_ISSUES is an unpinned mirror of the daemon cap — on drift the guard silently drops whole bindings and whole metadata events
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally; the packages/cli and packages/core unit suites also hit the harness infrastructure deadline on this contended host before completing (every failing file they stamped is unchanged in this PR)。
Test Plan(非阻断):78 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 102 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 1101 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 805 passed — this review observed 1820, 1722, 5189, 481, 94 passed; 115 passed — this review observed 1820, 1722, 5189, 481, 94 passed; and 1 more。
收敛姿态下延后(第 6 轮,非阻断)——已记录,本轮不要求修改:共 3 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
What this PR does
A session bound to a GitHub PR now also carries the issues that PR closes. The daemon's existing PR-state refresh sweep looks up each bound PR's closing references (the
Fixes #Nlinks GitHub already tracks) together with the issue state — open, completed, or not planned — and snapshots them onto the PR entry in the same sidecar, in place, without touching binding order or timestamps. The Web Shell session tooltip lists those issues under the PR rows with GitHub-style state icons (deduped when stacked PRs close the same issue), and sidebar search finds a session by an issue number, with or without the leading hash. The session-row badge is unchanged and still shows only the PR.The lookup is a single by-number GraphQL call per workspace per sweep, batched at 100 PRs. It runs for every non-merged binding (closing references change while a PR is open) and once for merged bindings that predate the snapshot, so a workspace whose bindings are all merged and snapshotted costs no
ghcall at all. The slim PR list query is left as it was: nesting closing references under it measurably slows it and still carries no issue state, while a by-number lookup also reaches PRs outside the 500-entry list window.ghexits non-zero when one alias is NOT_FOUND (a binding to another repository's same-numbered PR) yet still prints every other alias, so the wrapper parses that payload instead of failing the batch; the existing url guard keeps the wrong repository's PR from ever matching a foreign binding.Every sidecar-to-wire projection now goes through one shared helper, the bridge keeps the daemon-derived snapshot across a client re-bind of the same PR, and the SDK's runtime guard validates issue entries with the same http(s)/length/control-character rules as PR urls. Clients cannot bind issues; the snapshot is daemon-derived only.
Why it's needed
The sidebar could answer "which session produced PR #N" (#9729, #10258) but not "which session is working on issue #N", and the maintainer flow — bugfix, triage, autofix's develop-issue mode — starts from an issue and ends in a PR. There is no structured place in the Web Shell where an issue is bound the way a PR is bound from the Git dialog, and PRs and issues share one number space, so inferring the issue from prompts or branch names would misbind. GitHub's own closing references are the one high-precision source, and they come for free from the PR the session already bound.
Reviewer Test Plan
How to verify
gh authconfigured, bind a session to an open PR whose body saysFixes #N(create it from the Git dialog, or run the backfill route). Wait for the sweep (60s after daemon start, then every 5 minutes). Expected: the session's.pr.jsonentry gains anissueslist with the issue's number, url, andstate: "open"; the tooltip showsIssue #Nunder the PR row with a green circle-dot icon; typingNor#Nin the sidebar search finds the session.Issue #N · Completedto screen readers; the next sweep issues noghcall for that workspace.Fixes #Nline. Expected: the next sweep updates the list; an unchanged list does not rewrite the sidecar.Unit coverage: the sidecar schema (invalid issue url/state or more than 10 issues voids the sidecar), same-PR re-bind keeping the snapshot, in-place writes with or without a state, the GraphQL wrapper's state mapping / NOT_FOUND handling / chunking / failure kinds, the sweep's catch-up and no-call paths, the session list preferring sidecar issues over the live entry, the bridge seed/re-bind paths, the SDK guard, and the tooltip/search rendering.
Evidence (Before & After)
Real sweep from the built daemon against this repository with the real
ghCLI, one session bound to a merged PR (#10278, the fix PR for issue #10270), a foreign-repository PR #42, and an open PR (#10303, the PR for issue #10293), run twice:Sidecar after round 1 (before: the same three entries with no
issuesfield):[ { "number": 10278, "url": "https://github.com/QwenLM/qwen-code/pull/10278", "state": "merged", "issues": [{ "number": 10270, "url": "https://github.com/QwenLM/qwen-code/issues/10270", "state": "completed" }] }, { "number": 42, "url": "https://github.com/other/repo/pull/42", "state": "open" }, { "number": 10303, "url": "https://github.com/QwenLM/qwen-code/pull/10303", "state": "open", "issues": [{ "number": 10293, "url": "https://github.com/QwenLM/qwen-code/issues/10293", "state": "open" }] } ]Query cost measured on this repository:
gh pr list --state all --limit 500withclosingIssuesReferencesnested takes 6.7s versus 4.9s without (theghtimeout is 10s), which is why the list query is untouched and the issues come from a separate by-number lookup (~1s for four PRs).Round 2 (
75c5f88), with aghshim logging every invocation — one session holding a foreign open PR #42, a foreign merged PR #43, the merged #10278 and the open #10303: the first sweep runs the list query and one GraphQL lookup for[10278, 10303]only (the foreign open binding is filtered out by the repository prefix, the foreign merged one converges locally without a lookup); the second sweep looks up[10303]only:Round 3 (
4e18594), realghagain: a merged binding spelledhttp://www.github.com/QwenLM/qwen-code/pull/10278/filesreceives issue #10270 (completed) under its own url instead of being retired as foreign; and in agit initdirectory with no remote, a merged binding converges after exactly oneghcall (error parsing "owner" value: no git remotes found→ structural), rounds 2–3updated: 0in ~1ms:Convergence for a binding the repository cannot resolve (review round 1): one session bound only to a merged foreign-repository PR, real
gh, three sweeps — round 1 makes one lookup and writes the converging empty snapshot, later rounds make noghcall at all:Tooltip rendering is covered by jsdom tests (icon classes, link hrefs, dedupe, sr-only state text); no browser screenshot yet.
Tested on
Environment (optional)
Unit tests per package, plus the built daemon run directly against this repository with the real
ghCLI (macOS, gh 2.96.0).Risk & Scope
gh api graphqlcall per workspace per sweep whenever a binding is open/closed or lacks a snapshot (measured ~1–3s); the first sweep after upgrade looks up every legacy binding once, batched at 100. An issue reopened after its PR merged is not tracked, the same "merged is terminal" tradeoff the PR state already makes./bugfix #N), inferring issues from prompts, branches, or commit trailers, showing issues in the session-row badge, and fetching issues immediately in the backfill route (the next sweep covers it). Windows/Linux not run locally. No browser screenshot of the tooltip.issuesis optional on the wire and in the sidecar; older sidecars stay valid and are enriched by the first sweep.Linked Issues
Builds on #9729 (PR bindings, backfill, state refresh) and #10258 (GitHub-style state icons).
中文说明
这个 PR 做了什么
绑定了 GitHub PR 的会话现在也会带上该 PR 关闭的 issue。daemon 已有的 PR 状态刷新定时器会顺带查询每个已绑 PR 的 closing references(GitHub 已经跟踪的
Fixes #N链接)及 issue 状态——open、completed 或 not planned——并原地快照到同一个 sidecar 的 PR 条目上,不改变绑定顺序和时间戳。Web Shell 会话 tooltip 在 PR 行下方用 GitHub 风格的状态图标列出这些 issue(stacked PR 关闭同一 issue 时去重),侧栏搜索可以按 issue 号找到会话,带不带#都行。会话行 badge 不变,仍只显示 PR。查询是每 workspace 每轮一条按编号的 GraphQL 调用,每批 100 个 PR。所有未合入的绑定都会查(PR 开放期间 closing references 会变),已合入但尚无快照的绑定只补一次,所以全部已合入且已有快照的 workspace 完全不发
gh调用。slim 的 PR 列表查询保持原样:把 closing references 嵌套进去会明显拖慢它且仍不带 issue 状态,而按编号查询还能覆盖 500 条列表窗口之外的 PR。当某个别名 NOT_FOUND(绑定指向别的仓库的同号 PR)时gh以非零退出码返回,但 stdout 仍带其它别名的完整数据,包装器照常解析而不是让整批失败;已有的 url 守卫保证别的仓库的 PR 永远不会匹配到外仓绑定。所有 sidecar 到线协议的投影统一走一个共享助手,bridge 在客户端重绑同一 PR 时保留 daemon 派生的快照,SDK 的运行时守卫用与 PR url 相同的 http(s)/长度/控制字符规则校验 issue 条目。客户端不能绑定 issue,快照只由 daemon 派生。
为什么需要
侧栏能回答"哪个会话产出了 PR #N"(#9729、#10258),但回答不了"哪个会话在处理 issue #N",而维护者的流程——bugfix、triage、autofix 的 develop-issue 模式——从 issue 出发、以 PR 收口。Web Shell 里没有像 Git 对话框绑定 PR 那样的结构化 issue 绑定入口,PR 与 issue 又共用一个编号空间,从提示词或分支名推断会误绑。GitHub 自己的 closing references 是唯一高精度的来源,而且会话已绑定的 PR 免费带来。
评审测试计划
如何验证
gh auth的 workspace 里,把会话绑定到一个正文写有Fixes #N的开放 PR(从 Git 对话框创建,或跑回填路由)。等待 sweep(daemon 启动后 60s,之后每 5 分钟)。预期:该会话.pr.json条目多出issues列表,含 issue 编号、url 和state: "open";tooltip 在 PR 行下显示Issue #N和绿色 circle-dot 图标;侧栏搜索输入N或#N能找到该会话。Issue #N · Completed;下一轮 sweep 对该 workspace 不再发gh调用。Fixes #N。预期:下一轮 sweep 更新列表;列表不变时不重写 sidecar。单测覆盖:sidecar schema(非法 issue url/状态或超过 10 个 issue 会使 sidecar 失效)、同 PR 重绑保留快照、带/不带状态的原地写入、GraphQL 包装器的状态映射 / NOT_FOUND 处理 / 分批 / 失败类型、sweep 的补齐与零调用路径、会话列表以 sidecar 的 issues 为准、bridge 的 seed/重绑路径、SDK 守卫,以及 tooltip/搜索渲染。
证据(前后对比)
用构建好的 daemon 配真实
ghCLI 对本仓库跑真实 sweep:一个会话绑定了已合入的 PR(#10278,关闭了 #10270)、外仓 PR #42 和开放 PR(#10303,引用 #10293),跑两轮:第一轮后的 sidecar(之前:同样三条、没有
issues字段):[ { "number": 10278, "url": "https://github.com/QwenLM/qwen-code/pull/10278", "state": "merged", "issues": [{ "number": 10270, "url": "https://github.com/QwenLM/qwen-code/issues/10270", "state": "completed" }] }, { "number": 42, "url": "https://github.com/other/repo/pull/42", "state": "open" }, { "number": 10303, "url": "https://github.com/QwenLM/qwen-code/pull/10303", "state": "open", "issues": [{ "number": 10293, "url": "https://github.com/QwenLM/qwen-code/issues/10293", "state": "open" }] } ]在本仓库实测的查询成本:
gh pr list --state all --limit 500嵌套closingIssuesReferences需 6.7s,不嵌套 4.9s(gh超时 10s),因此列表查询不动,issue 走单独的按编号查询(4 个 PR 约 1s)。仓库解析不到的绑定的收敛(评审第一轮):一个会话只绑定了一个已合入的外仓 PR,真实
gh,跑三轮——第一轮一次查询并写入收敛用的空快照,之后各轮完全不发gh调用:tooltip 渲染由 jsdom 测试覆盖(图标 class、链接 href、去重、sr-only 状态文本);暂无浏览器截图。
测试平台
环境(可选)
各包单测,加上构建好的 daemon 直接对本仓库用真实
ghCLI 运行(macOS,gh 2.96.0)。风险与范围
gh api graphql调用(实测约 1–3s);升级后首轮对每个存量绑定查一次,每批 100 个。PR 合入后 issue 被 reopen 不再跟踪,与 PR 状态"merged 是终态"的取舍一致。/bugfix #N)、从提示词/分支/commit trailer 推断 issue、会话行 badge 显示 issue、回填路由立即抓取 issue(下一轮 sweep 兜底)。Windows/Linux 未在本地运行。tooltip 无浏览器截图。issues在线协议和 sidecar 中均为可选;旧 sidecar 保持有效,首轮 sweep 补齐。关联 Issue
基于 #9729(PR 绑定、回填、状态刷新)与 #10258(GitHub 风格状态图标)。