Skip to content

fix(triage): make unattended PR review static — read CI via API, never run PR code - #7646

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
wenshao:triage/static-review-ci-evidence
Jul 24, 2026
Merged

fix(triage): make unattended PR review static — read CI via API, never run PR code#7646
wenshao merged 1 commit into
QwenLM:mainfrom
wenshao:triage/static-review-ci-evidence

Conversation

@wenshao

@wenshao wenshao commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

What & why

During Stage 2 the triage skill instructed the review agent to run the PR's tests (npm, tmux) in the CI worktree. Two problems in an unattended run:

  1. It executes untrusted PR code in a job whose environment carries a write PAT — a prompt-injected PR could reach that token.
  2. The "tests pass" evidence was self-run, not the PR's own CI. On feat(channels): GitHub polling adapter with notification-as-wakeup architecture #7632 the Stage 2 comment even relayed the author's self-reported 8/8 E2E as if it were verification.

Change

Scope testing behavior by trigger:

  • Unattended CI (GITHUB_EVENT_NAME set): never build or run PR-derived code. Stage 2 test evidence now comes from the PR's own CI checks via the API — check-runs plus the failing job's log excerpt. Real-scenario TUI coverage is left to the isolated @qwen-code /tmux job.
  • Local invocation only (no GITHUB_EVENT_NAME): drive the app in tmux exactly as before.

Also:

  • Never present the author's self-reported results under a testing heading — attribute them as a claim if referenced at all.
  • Fetch the CHANGELOG via gh api instead of curl.

Verification

Ran the real triage skill end-to-end against #7632 with the qwen3.8-max-preview model (the CI review model) through a write-blocking gh shim, so nothing posted to the PR. Across two model runs the agent:

  • executed zero PR-code commands (no npm/node/vitest),
  • pulled CI evidence via check-runs + the failing job's logs exactly as intended,
  • completed all three stages and produced a clean Stage 2 with a real CI check table.

Merge order

The companion workflow PR #7647 enforces these restrictions with tool/permission settings that deny npm/node/curl. Merge this PR first so those denials never surprise the agent.

中文说明

改动内容与原因

triage skill 在 Stage 2 让 review agent 在 CI worktree 里跑 PR 的测试(npm、tmux)。无人值守运行下有两个问题:

  1. 执行了不可信的 PR 代码,而该 job 的环境里带着有写权限的 PAT —— 被 prompt 注入的 PR 有可能拿到这个 token。
  2. "测试通过"的证据是自己跑出来的,不是 PR 自己的 CI。在 feat(channels): GitHub polling adapter with notification-as-wakeup architecture #7632 上,Stage 2 评论甚至把作者自报的 8/8 E2E 当成了验证结果转述。

改动

按触发来源区分测试行为:

  • 无人值守 CI(设置了 GITHUB_EVENT_NAME):绝不构建或运行 PR 代码。Stage 2 的测试证据改为通过 API 读 PR 自身的 CI check —— check-runs 加上失败 job 的日志片段。真实场景 TUI 验证交给隔离的 @qwen-code /tmux job。
  • 仅本地调用(无 GITHUB_EVENT_NAME):照旧用 tmux 驱动应用。

另外:

  • 绝不把作者自报的结果放在测试小节里当证据 —— 如需引用,须明确标注为"作者声称"。
  • CHANGELOG 改用 gh api 拉取,不用 curl

验证

qwen3.8-max-preview(CI 实际使用的 review 模型)对 #7632 端到端跑了真实 triage skill,并通过一个写拦截的 gh shim 保证不向 PR 发任何内容。两轮模型运行中,agent:

  • PR 代码执行(没有 npm/node/vitest);
  • 完全按设计通过 check-runs + 失败 job 日志获取 CI 证据;
  • 跑完全部三个 stage,Stage 2 产出了干净的真实 CI check 表。

合并顺序

配套的 workflow PR #7647 用 tool/permission 设置来强制这些限制(会 deny npm/node/curl)。请先合并本 PR,这样那些 deny 才不会让 agent 意外受阻。

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template: the body uses its own headings ("What & why", "Change", "Verification", "Merge order") rather than the template's exact sections, but the content is thorough and covers the intent. Not blocking on heading names for a skill-docs PR.

Problem: observed, with evidence. The PR references #7632 where the triage agent relayed an author's self-reported 8/8 E2E as if it were verification, and identifies a concrete security vector — executing untrusted PR code in a CI job that carries a write PAT. This is a real, well-known attack surface (prompt injection → token exfiltration), not theoretical hardening.

Direction: clearly aligned. This hardens the maintainer's own triage tooling against a real security risk and improves the reliability of test evidence. CHANGELOG: no direct reference (this is internal tooling, not a product feature), but the area is directly relevant to the project's CI/automation infrastructure.

Size: not applicable — no core paths touched. All 3 changed files are under .qwen/skills/triage/.

Approach: the scope feels right. Scoping testing behavior by trigger (CI reads results via API, local runs tmux as before) is the minimal, well-targeted fix. The companion workflow PR handles enforcement via tool/permission denials — this PR handles the skill instructions. Every edit in the diff serves the stated goal; no drive-by changes. The curlgh api switch for CHANGELOG is a natural part of the "don't spawn unnecessary subprocesses" approach.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板:PR 正文使用了自己的标题("What & why"、"Change"、"Verification"、"Merge order"),而非模板的精确章节名,但内容详尽,覆盖了模板的意图。对于 skill 文档类 PR,不因标题名称而阻止。

问题:已观测到,有证据。PR 引用了 #7632,其中 triage agent 将作者自报的 8/8 E2E 结果当作验证转述;并指出了具体的安全风险——在携带写权限 PAT 的 CI job 中执行不可信的 PR 代码。这是真实的攻击面(prompt 注入 → token 泄露),不是理论性加固。

方向:明确对齐。这加固了维护者自己的 triage 工具,防范真实的安全风险,并提升测试证据的可靠性。CHANGELOG:无直接引用(这是内部工具,不是产品功能),但该领域与项目的 CI/自动化基础设施直接相关。

规模:不适用——未触及核心路径。全部 3 个变更文件都在 .qwen/skills/triage/ 下。

方案:范围合理。按触发来源区分测试行为(CI 通过 API 读取结果,本地照旧跑 tmux)是最小且精准的修复。配套的 workflow PR 通过 tool/permission 设置来强制执行——本 PR 处理 skill 指令。diff 中每处改动都服务于既定目标,没有顺手改动。curlgh api 的切换是"不产生不必要子进程"方案的自然组成部分。

进入代码审查 🔍

Qwen Code · qwen3.8-max-preview

Reviewed at ae90226e7eb04c8d6c6aea1566a9d9486044bda2 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Code review

Independent proposal: given the problem (triage agent executes untrusted PR code in a CI job carrying a write PAT, and relays author self-reported results as evidence), I would: (1) add a clear prohibition rule in SKILL.md, (2) replace tmux-based testing in CI with reading CI check results via the GitHub API, (3) scope tmux to local invocation only, (4) update issue-workflow.md consistently, (5) add a rule against presenting author's self-reported results as evidence, (6) switch curl to gh api for CHANGELOG.

Comparison with the diff: the PR matches this proposal exactly. No simpler path missed. All suggestions from the previous review round have been addressed in the latest push:

  • --paginate + --jq multi-page flattening: now uses jq -s 'add' (this repo has hit 500+ checks on a commit, so the multi-page case was real).
  • Third-party check URL filtering: select(.details_url | test("/job/")) before stripping the job id, so a failing Codecov/SonarCloud check can't shadow the real Actions job.
  • CHANGELOG fetch: switched to -H "Accept: application/vnd.github.raw+json" — no base64 step, no 1 MB size cap, BSD-portable.
  • Trust boundary warning: added — check names/conclusions are GitHub-set (trusted), log body is PR-controlled (untrusted).
  • Inaccurate deny-list claims: reworded to reference the SKILL.md static-review rule, not a non-existent config.

Walkthrough of the three files:

  • SKILL.md — the new "⛔ Never execute PR-derived code" rule is well-placed in the Rules section and comprehensive: covers npm/node/npx/interpreters/build/test, gh pr checkout, git apply, and PR-added scripts. The worktree exception is updated to clarify CI has no tmux exception. The old "Tmux screenshots" section is replaced with a "Testing evidence" section that correctly scopes CI (API reads) vs. local (tmux capture). The author-claim attribution rule directly addresses the feat(channels): GitHub polling adapter with notification-as-wakeup architecture #7632 incident.
  • pr-workflow.md — new Stage 2b provides concrete, correct gh api commands for check-runs and failing job logs, with sensible polling guidance and a "CI still running" fallback. The jq -s 'add' flattening and /job/ URL filtering are correct. Old 2b becomes 2c, scoped to local invocation. The curlgh api CHANGELOG switch uses the raw accept header — correct and portable. The Stage 2 comment structure and "BEFORE POSTING" checklist are updated consistently.
  • issue-workflow.md — tmux testing scoped to local invocation with a clear CI fallback (static analysis + explicit "not executed" note). Correctly references the SKILL.md rule, not a deny list.

No critical blockers. No AGENTS.md violations. The changes are minimal, internally consistent, and follow the existing style.

CI test evidence

CI checks at review time (commit ae90226):

Status Conclusion Check
completed success precheck-pr / precheck
completed success Classify PR
completed success authorize
completed success label
completed success Remind on force-push
in_progress Test (ubuntu-latest, Node 22.x)
completed skipped Test (windows-latest, Node 22.x)
completed skipped Test (macos-latest, Node 22.x)
completed skipped Integration Tests (CLI, No Sandbox)

Precheck passed. Unit tests still running at review time — this is a docs-only PR touching .qwen/skills/triage/ with no production code changes, so test outcomes are not expected to be affected. The previous commit (f57a460) had all checks green.

Real-scenario testing

Not applicable — this PR modifies triage skill documentation files, not product code. Running the CLI in tmux would not exercise the changed behavior. The author reports having tested the skill end-to-end against #7632 with a write-blocking gh shim (zero PR-code commands executed, CI evidence fetched via API as designed) — noted as the author's claim, not as verified evidence.

Not verified: the companion workflow PR #7647 that enforces these restrictions via tool/permission denials (mentioned in the PR body as a separate PR to merge after this one).

中文说明

代码审查

独立方案: 针对该问题(triage agent 在携带写权限 PAT 的 CI job 中执行不可信的 PR 代码,并将作者自报结果当作证据转述),我会:(1) 在 SKILL.md 中添加明确的禁止规则,(2) 将 CI 中的 tmux 测试替换为通过 GitHub API 读取 CI check 结果,(3) 将 tmux 限定为仅本地调用,(4) 同步更新 issue-workflow.md,(5) 添加禁止将作者自报结果作为证据的规则,(6) 将 CHANGELOG 的 curl 切换为 gh api

与 diff 对比: PR 与该方案完全一致。没有遗漏更简路径。上一轮审查的所有建议已在最新推送中采纳:

  • --paginate + --jq 多页展平:现使用 jq -s 'add'(本仓库曾在单个 commit 上达到 500+ check,多页场景是真实的)。
  • 第三方 check URL 过滤:在提取 job id 前先 select(.details_url | test("/job/")),避免 Codecov/SonarCloud 等第三方失败遮蔽真正的 Actions job。
  • CHANGELOG 获取:切换为 -H "Accept: application/vnd.github.raw+json"——无 base64 步骤、无 1 MB 大小限制、BSD 可移植。
  • 信任边界警告:已添加——check 名称/结论是 GitHub 设置的(可信),日志正文是 PR 控制的(不可信)。
  • 不准确的 deny-list 声称:已改写为引用 SKILL.md 静态审查规则,而非不存在的配置。

三个文件的走查:

  • SKILL.md — 新的"⛔ 绝不执行 PR 代码"规则放在 Rules 小节,位置恰当,覆盖全面。worktree 例外已更新,明确 CI 中没有 tmux 例外。旧的"Tmux screenshots"小节替换为"Testing evidence"小节,正确区分 CI(API 读取)和本地(tmux 捕获)。作者声称归属规则直接回应了 feat(channels): GitHub polling adapter with notification-as-wakeup architecture #7632 事件。
  • pr-workflow.md — 新 Stage 2b 提供了具体且正确的 gh api 命令,jq -s 'add' 展平和 /job/ URL 过滤均正确。curlgh api 的 CHANGELOG 切换使用 raw accept header——正确且可移植。
  • issue-workflow.md — tmux 测试限定为本地调用,正确引用 SKILL.md 规则而非 deny list。

无关键阻塞项。无 AGENTS.md 违规。改动最小化、内部一致,遵循现有风格。

CI 测试证据

审查时的 CI check(commit ae90226):precheck 通过,单元测试仍在运行中(仅涉及 .qwen/skills/triage/ 的文档 PR,未变更生产代码)。前一 commit(f57a460)所有 check 均为绿色。

真实场景测试

不适用——本 PR 修改的是 triage skill 文档文件,不是产品代码。作者报告已对 #7632 进行了端到端测试——记为作者声称,非已验证证据。

未验证:配套的 workflow PR #7647

Qwen Code · qwen3.8-max-preview

Reviewed at ae90226e7eb04c8d6c6aea1566a9d9486044bda2 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 5/5 — clean across every stage; the security rationale is sound, the implementation is minimal and correct, and all prior review feedback has been addressed.

This PR fixes a real problem with evidence: the triage agent was executing untrusted PR code in a CI job carrying a write PAT, and #7632 showed the author-claim relay issue in action. The fix is exactly what I would have proposed — scope testing by trigger, read CI results via API in unattended runs, keep tmux for local only. Every edit in the diff earns its place; there's no bloat, no drive-by refactors, no over-engineering.

The latest push addressed every suggestion from the previous review round: the jq -s 'add' flattening for multi-page check-runs, the /job/ URL filter for third-party checks, the raw accept header for CHANGELOG (no base64, no size cap), the trust boundary warning, and the reworded deny-list references. The one "Critical" finding from the prior review — /job/ vs /jobs/ — was a false positive; the author verified 418/418 live check-runs use singular /job/ in details_url.

The companion workflow PR #7647 (tool/permission denials) is the enforcement side; this PR is the instruction side. Merging this first, as the PR body suggests, makes sense — the agent should know the rules before the enforcement kicks in.

If I had to maintain this in six months, I'd thank the author. The triage workflow is safer and the test evidence model is more honest.

中文说明

置信度:5/5 — 每个阶段都很干净;安全理由充分,实现最小且正确,所有先前审查反馈均已处理。

这个 PR 修复了一个有证据的真实问题:triage agent 在携带写权限 PAT 的 CI job 中执行不可信的 PR 代码,#7632 展示了作者声称结果被转述的实际案例。修复方案与我的独立提案完全一致——按触发来源区分测试行为,无人值守运行通过 API 读取 CI 结果,tmux 仅限本地。diff 中每处改动都有其必要性,没有冗余、没有顺手重构、没有过度工程。

最新推送处理了上一轮审查的所有建议:多页 check-runs 的 jq -s 'add' 展平、第三方 check 的 /job/ URL 过滤、CHANGELOG 的 raw accept header(无 base64、无大小限制)、信任边界警告、以及改写的 deny-list 引用。上一轮的"Critical"发现——/job/ vs /jobs/——是误报;作者验证了 418/418 个真实 check-run 的 details_url 使用单数 /job/

配套的 workflow PR #7647(tool/permission 拒绝)是执行侧;本 PR 是指令侧。如 PR 正文所建议,先合并本 PR 是合理的——agent 应该先知道规则,然后才启动强制执行。

如果半年后我要维护这些文件,我会感谢作者。triage 工作流更安全了,测试证据模型也更诚实了。

Qwen Code · qwen3.8-max-preview

Reviewed at ae90226e7eb04c8d6c6aea1566a9d9486044bda2 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot 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.

LGTM, looks ready to ship. ✅

Comment on lines +356 to +357
| sed 's#.*/job/##')
[ -n "$JOB_ID" ] && gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" | tail -c 15000

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.

[Critical] The sed pattern uses /job/ (singular) but GitHub Actions details_url uses /jobs/ (plural) — e.g. …/actions/runs/12345/jobs/67890. The expression s#.*/job/## does not match, so JOB_ID becomes the full URL string. The [ -n "$JOB_ID" ] guard passes (non-empty), and gh api "repos/$REPO/actions/jobs/<full-URL>/logs" 404s. The Stage 2 comment is posted without the failing job's log excerpt — the very evidence this section is designed to surface. — Failure scenario: any PR with a failing CI check → log excerpt silently missing from the triage comment.

Suggested change
| sed 's#.*/job/##')
[ -n "$JOB_ID" ] && gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" | tail -c 15000
| sed 's#.*/jobs/##')
[ -n "$JOB_ID" ] && gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" | tail -c 15000

Also update the comment on line 353 from "after /job/" to "after /jobs/".

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Verified against the live API — the check-run details_url for an Actions job uses singular /job/, not /jobs/:

$ gh api ".../check-runs?per_page=100" --paginate --jq '.check_runs[].details_url' | grep -oE '/jobs?/' | sort | uniq -c
    418 /job/

(418/418 real check-runs on this repo — e.g. .../actions/runs/30064620665/job/89393060859.) The gh api .../actions/jobs/<id>/logs endpoint is plural, but the details_url path segment is singular, so sed 's#.*/job/##' extracts the id correctly. Confirmed end-to-end in a real triage run (extracted 89384467683, then gh api .../jobs/89384467683/logs returned the log). Switching to /jobs/ would make the strip a no-op and 404 the fetch — the exact failure this comment describes, but inverted.

Keeping /job/. I did adopt the caching half of this thread (fetch check-runs once, read locally) in the latest push.

— via Claude Code (Opus 4.8)

Comment thread .qwen/skills/triage/references/pr-workflow.md Outdated
Comment thread .qwen/skills/triage/references/issue-workflow.md Outdated
Comment thread .qwen/skills/triage/references/pr-workflow.md Outdated
Comment thread .qwen/skills/triage/references/pr-workflow.md
@wenshao
wenshao force-pushed the triage/static-review-ci-evidence branch from 4b08bf1 to f57a460 Compare July 24, 2026 05:35
@wenshao
wenshao requested a review from Copilot July 24, 2026 05:35
@github-actions

Copy link
Copy Markdown
Contributor

Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration.

中文

请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wenshao

wenshao commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the review — addressed in the latest push (force-updated the branch).

Fixed:

  • Inaccurate curl deny-list claim (pr-workflow.md:176, issue-workflow.md:105): reworded. Within this PR the enforcement is the SKILL.md static-review rule, not a denylist — dropped the "deny-listed in the CI agent settings" wording that sent readers hunting for config that isn't in this PR. (The actual denylist lives in the companion fix(triage): actually restrict the CI review agent's tools #7647; asserting it here was wrong.)
  • Duplicate check-runs fetch: now fetched once into a temp file and read locally for both the overview table and the failing-job lookup — one paginated round-trip instead of two.
  • CI log body is PR-controlled: added a trust-boundary note — check names and conclusions are GitHub-set (trusted); the log body is attacker-controlled on a fork and must not drive the PR-caused-vs-pre-existing classification.

Not changed — false positive:

  • The [Critical] /job/ vs /jobs/: details_url is singular /job/ (verified 418/418 on live data; extraction confirmed in a real run). Details in my inline reply. Applying the suggested /jobs/ would break the currently-working extraction.

— via Claude Code (Opus 4.8)

@qwen-code-ci-bot qwen-code-ci-bot 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.

Reviewed. Suggestions are inline. Not reviewed: Agent 0: Issue fidelity & root-cause ownership, Agent 4: Performance & efficiency, Agent 7: Build & test verification — its prompt was built, but no agent on record was launched with it. Not reviewed: reverse audit — an auditor ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and what the agent was actually asked is not what this skill certifies. Not reviewed: verification — a verifier ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and the posted findings cannot be counted as verified against it.

— qwen3.7-max via Qwen Code /review

Comment thread .qwen/skills/triage/references/pr-workflow.md Outdated
Comment thread .qwen/skills/triage/references/pr-workflow.md Outdated
Comment thread .qwen/skills/triage/references/pr-workflow.md Outdated
@wenshao

wenshao commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

…r run PR code

The triage skill instructed the review agent to run the PR's tests (npm/tmux)
in the CI worktree. That executes untrusted PR code in a job whose environment
carries a write PAT, and the "tests pass" evidence it produced came from
self-run commands rather than the PR's own CI.

Scope the behavior by trigger:

- Unattended CI runs (GITHUB_EVENT_NAME set): never build or run PR-derived
  code. Stage 2 test evidence comes from the PR's own CI checks via the API
  (check-runs + the failing job's log excerpt). Real-scenario TUI coverage is
  left to the isolated `@qwen-code /tmux` job.
- Local invocation only (no GITHUB_EVENT_NAME): drive the app in tmux as before.

Also: never present the author's self-reported results under a testing heading —
attribute them as a claim if referenced at all; and fetch the CHANGELOG via
`gh api` instead of `curl`.

The companion workflow change that enforces these restrictions with tool/permission
settings should merge after this, so its npm/curl denials never surprise the agent.
@wenshao
wenshao force-pushed the triage/static-review-ci-evidence branch from f57a460 to ae90226 Compare July 24, 2026 06:25
@wenshao
wenshao requested a review from Copilot July 24, 2026 06:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wenshao

wenshao commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up — the re-review caught three real issues in the caching change from the last push; all adopted:

  • --paginate + --jq emits one array per page → flattened with jq -s 'add'. This repo has hit 500+ checks on a commit, so the multi-page case was real.
  • sed on a non-Actions details_url (Codecov/SonarCloud etc.) → now filters select(.details_url | test("/job/")) first, so a third-party failure can't shadow the real Actions job.
  • Contents API omits content for files >1 MB, plus base64 -d isn't BSD-portable → CHANGELOG fetch switched to -H "Accept: application/vnd.github.raw+json" (raw body, no base64, no size cap).

Resolving the addressed threads.

— via Claude Code (Opus 4.8)

@qwen-code-ci-bot qwen-code-ci-bot 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.

LGTM, looks ready to ship. ✅


```bash
# Fetch check-runs ONCE for the reviewed commit, then read locally. --paginate
# runs --jq per page, so it emits one array per page; `jq -s 'add'` flattens

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.

[Suggestion] jq -s 'add' flattens paginated arrays correctly when each page outputs a valid array. If an API page returns a non-array (rare API error), add will fail. Consider adding // empty fallback or wrapping in error handling — but given --paginate + --jq executes jq per page independently, this is an extremely unlikely edge case. Non-blocking.

— qwen3.8-max-preview via Qwen Code /review

@yiliang114 yiliang114 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.

LGTM. Critical security hardening — prevents executing untrusted PR code in CI. Trust boundary well-defined (GitHub metadata trusted, log body untrusted). API-based CI evidence fetching is correct. One inline nit on jq pagination edge case.

— qwen3.8-max-preview via Qwen Code /review

@qwen-code-ci-bot qwen-code-ci-bot 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.

Reviewed. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

# runs --jq per page, so it emits one array per page; `jq -s 'add'` flattens
# them into a single merged array (this repo has hit 500+ checks on a commit):
gh api "repos/$REPO/commits/$HEAD_SHA/check-runs?per_page=100" --paginate \
--jq '.check_runs' | jq -s 'add' > /tmp/triage-checks.json

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.

[Suggestion] jq -s 'add' produces null when gh api produces no stdout (e.g. gh api exits non-zero due to bad SHA, network error, or expired token). The downstream jq -r '.[] | ...' then fails with Cannot iterate over null (null). — Failure scenario: gh api error → empty stdout → jq -s 'add' yields null → every subsequent jq query on the file errors.

Suggested change
--jq '.check_runs' | jq -s 'add' > /tmp/triage-checks.json
--jq '.check_runs' | jq -s 'add // []' > /tmp/triage-checks.json

— qwen3.7-max via Qwen Code /review

# verified); third-party checks (Codecov, SonarCloud, …) point at their own
# domain, so filter to /job/ URLs before stripping the id — otherwise the first
# non-Actions failure yields a bogus job path and the real one goes unread:
JOB_ID=$(jq -r '[.[] | select(.conclusion == "failure") | select(.details_url | test("/job/"))][0].details_url // empty' \

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.

[Suggestion] select(.details_url | test("/job/")) errors when any failed check_run has details_url: null — jq's test() cannot match against null input (null cannot be matched, as it is not a string), aborting the entire filter so no JOB_ID is extracted. — Failure scenario: a failed third-party check with null details_url → jq exits 5 → JOB_ID empty → log fetch skipped → Stage 2 comment lacks the failing job's diagnostic excerpt.

Suggested change
JOB_ID=$(jq -r '[.[] | select(.conclusion == "failure") | select(.details_url | test("/job/"))][0].details_url // empty' \
JOB_ID=$(jq -r '[.[] | select(.conclusion == "failure") | select((.details_url // "") | test("/job/"))][0].details_url // empty' \

— qwen3.7-max via Qwen Code /review

# non-Actions failure yields a bogus job path and the real one goes unread:
JOB_ID=$(jq -r '[.[] | select(.conclusion == "failure") | select(.details_url | test("/job/"))][0].details_url // empty' \
/tmp/triage-checks.json | sed 's#.*/job/##')
[ -n "$JOB_ID" ] && gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" | tail -c 15000

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.

[Suggestion] [ -n "$JOB_ID" ] && … returns exit code 1 when $JOB_ID is empty (the common case when all checks pass or only third-party checks fail). An agent's shell execution layer may interpret this as a command failure and retry or abort. — Concrete cost: on the happy path (no failed Actions jobs), the agent sees a non-zero exit from a no-op.

Suggested change
[ -n "$JOB_ID" ] && gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" | tail -c 15000
if [ -n "$JOB_ID" ]; then gh api "repos/$REPO/actions/jobs/$JOB_ID/logs" | tail -c 15000; fi

— qwen3.7-max via Qwen Code /review

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.

4 participants