Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f51482f
feat(ci): route non-functional PRs to a triage-only review path (#7411)
yiliang114 Aug 14, 2026
75ced85
chore(ci): retrigger failed checks
yiliang114 Aug 15, 2026
b80a1a9
fix(ci): pin the triage-only skip to the triaged head SHA (#9219)
yiliang114 Aug 15, 2026
949e494
Merge remote-tracking branch 'origin/main' into HEAD
yiliang114 Aug 15, 2026
102cefb
fix(ci): authenticate triage-only markers
yiliang114 Aug 15, 2026
0760f41
fix(ci): drop the printf|grep pipes from the triage-only skip gate
yiliang114 Aug 15, 2026
1430f84
test(ci): align triage marker pin assertion
yiliang114 Aug 15, 2026
5c02b55
fix(ci): close triage-route review round (#9193)
yiliang114 Aug 15, 2026
a577d64
fix(ci): make skip-gate read failures diagnosable + correct doc ratio…
yiliang114 Aug 16, 2026
f9e2e1e
test(ci): pin --paginate on the skip gate's list reads (#9193)
yiliang114 Aug 16, 2026
ef9b6f4
fix(ci): carve whitespace-is-syntax files out of the triage-only rout…
yiliang114 Aug 16, 2026
5516a2a
docs(ci): cover the label-failure branch in the Stage 1f fail-closed …
yiliang114 Aug 16, 2026
8687fd9
Merge remote-tracking branch 'origin/main' into HEAD
yiliang114 Aug 16, 2026
742ed71
fix(ci): pin triage-only skips to base
yiliang114 Aug 16, 2026
ee43e1c
fix(ci): strip stale status/on-hold when the triage pin no longer mat…
yiliang114 Aug 16, 2026
0966c62
fix(ci): pin the on-hold marker to a dedicated comment and harden Sta…
yiliang114 Aug 16, 2026
e3f6a6d
fix(ci): honor triage retarget and manual holds
yiliang114 Aug 16, 2026
cc37df5
test(ci): pin triage skip label retention
yiliang114 Aug 16, 2026
3354b1a
test(ci): make the stale-label-removal failure branch reachable (#9193)
yiliang114 Aug 16, 2026
66645bc
fix(ci): close the edited-trigger race and the base TOCTOU (#9193)
yiliang114 Aug 16, 2026
3b87dae
fix(ci): authenticate the stale-marker invalidation as the marker author
yiliang114 Aug 16, 2026
090742c
Merge branch 'main' into feat/7411-triage-only-route
yiliang114 Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 122 additions & 2 deletions .github/workflows/qwen-code-pr-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- 'reopened'
- 'ready_for_review'
- 'review_requested'
- 'edited'
Comment thread
yiliang114 marked this conversation as resolved.
Comment thread
yiliang114 marked this conversation as resolved.
- 'closed'
issue_comment:
types: ['created']
Expand Down Expand Up @@ -62,11 +63,18 @@ concurrency:
# anyone who can request the bot without write permission. The per-run group
# costs only an occasional duplicate review when an authorized bot request
# lands while the lifecycle run for the same head still queues: compute,
# never a lost review. Comment/review events use per-run groups to avoid
# cancelling active reviews.
# never a lost review. The SAME carve-out applies to `edited` runs without a
# base change: they exit should_run=false at the step's early gate, so in
# the shared group a routine title/body edit could supersede a pending
# synchronize run and the pushed head would silently never review. Only
# base-retarget edits (changes.base) join the PR group — superseding a
# stale pending run there reviews live state: compute, never a lost
# review. Comment/review events use per-run groups to avoid cancelling
# active reviews.
group: >-
${{ github.event_name == 'pull_request_target' &&
github.event.action != 'review_requested' &&
(github.event.action != 'edited' || github.event.changes.base) &&
format('qwen-pr-review-pr-{0}', github.event.pull_request.number) ||
format('qwen-pr-review-run-{0}', github.run_id) }}
cancel-in-progress: "${{ github.event_name == 'pull_request_target' && (github.event.action == 'synchronize' || github.event.action == 'closed') }}"
Expand All @@ -83,6 +91,7 @@ jobs:
if: |-
github.event_name == 'pull_request_target' &&
github.event.action != 'closed' &&
(github.event.action != 'edited' || github.event.changes.base) &&
github.event.pull_request.head.repo.full_name != github.repository &&
(github.event.action != 'review_requested' ||
github.event.requested_reviewer.login == 'qwen-code-ci-bot')
Expand Down Expand Up @@ -262,6 +271,9 @@ jobs:
!cancelled() &&
(github.event_name != 'pull_request_target' ||
github.event.action != 'closed') &&
(github.event_name != 'pull_request_target' ||
github.event.action != 'edited' ||
github.event.changes.base) &&
(github.event_name != 'pull_request_target' ||
github.event.pull_request.head.repo.full_name == github.repository ||
needs.precheck-pr.outputs.decision == 'allow_triage') &&
Expand Down Expand Up @@ -377,6 +389,7 @@ jobs:
(github.event_name == 'pull_request_target' &&
github.event.pull_request.state == 'open' &&
!github.event.pull_request.draft &&
(github.event.action != 'edited' || github.event.changes.base) &&
needs.authorize.outputs.should_review == 'true' &&
Comment thread
yiliang114 marked this conversation as resolved.
((github.event.action == 'review_requested' &&
github.event.requested_reviewer.login == needs.review-config.outputs.bot_login) ||
Expand Down Expand Up @@ -613,6 +626,8 @@ jobs:
id: 'context'
env:
TRIGGER_BODY: "${{ github.event.comment.body || github.event.review.body || '' }}"
# Label lookup and stale-label removal for the triage-only skip below.
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
run: |-
set -euo pipefail
DEFAULT_TIMEOUT_MINUTES=180
Expand Down Expand Up @@ -658,6 +673,11 @@ jobs:
fi
PR_NUMBER="${{ github.event.pull_request.number }}"
REVIEW_MODE="comment"
if [ "${{ github.event.action }}" = "edited" ] &&
[ "${{ github.event.changes.base && 'true' || 'false' }}" != "true" ]; then
Comment thread
yiliang114 marked this conversation as resolved.
echo "should_run=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "${{ github.event_name }}" = "pull_request_target" ] &&
[ "${{ github.event.action }}" != "review_requested" ]; then
AUTO_REVIEW=true
Expand All @@ -667,6 +687,106 @@ jobs:
exit 1
fi

# Triage-only route (#7411): the triage skill routes PRs it
# classifies as behavior-neutral maintenance (renames, formatting,
# comment/JSDoc-only) to a triage summary instead of the full
# review, marking them with `status/on-hold` (pr-workflow.md,
# Stage 1f). The automatic lane honours that mark; check the LIVE
# labels, not the event payload — the label lands after the event
# fires, and this step runs again after the review delay. Explicit
# asks (/review comments, review_requested, dispatch) never reach
# this check, and removing the label re-enables the automatic lane
# on the next push. A failed label read skips the optimisation and
# lets the review run (fail open: the worst case is one extra full
# review, never a silently skipped one).
#
# The label alone is NOT sufficient (#9193): Stage 1f applies it
# once at open time and, until a stale pin strips it, it never
# leaves the exempt list — so a PR that earns it with a benign v1
# could silently skip review for every later push of arbitrary
# content. Skip only while the triage run's marker comment is
# pinned to the LIVE head/base SHA pair; a push or base retarget
# after triage moves them (triage re-runs on neither), stale pin ->
# the new diff goes to the review lane, which also strips the
Comment thread
yiliang114 marked this conversation as resolved.
# label. A manually applied "parked" label has no marker and never
# skips. Unreadable head/base fails open like the label read.
if [ "$AUTO_REVIEW" = "true" ]; then
# No `printf | grep -q` pipes below: under `set -o pipefail`
# the -q consumer exits at the first match and closes the
# pipe, so the producer (printf builtin or gh) dies on EPIPE
# with 141 and pipefail surfaces that as FALSE even when the
# marker matches — on busy PRs (>64 KB of bot comment bodies)
# the skip would silently never fire. Herestrings and [[ == ]]
# pattern matches have no pipe and no early-exit producer.
#
# Read failures stay fail-open but are never silent: each
# failed read writes its own summary line, so a skip disabled
# by an API failure, rate-limit window, or token regression is
# diagnosable from the run artifacts — and a failed read is
# never reported as a stale pin, which would assert a mismatch
# that was never observed (R5-4, #9193 review).
LABELS_READ_OK=true
LABELS="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels?per_page=100" --paginate --jq '.[].name' 2>/dev/null)" || LABELS_READ_OK=false
Comment thread
yiliang114 marked this conversation as resolved.
if [ "$LABELS_READ_OK" = false ]; then
echo "status/on-hold skip gate: label read failed — failing open, running the review lane (#9193)." >> "$GITHUB_STEP_SUMMARY"
elif grep -Fxq 'status/on-hold' <<< "$LABELS"; then
PR_REFS="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" --jq '[.head.sha, .base.sha] | @tsv' 2>/dev/null || true)"
IFS=$'\t' read -r HEAD_SHA BASE_SHA <<< "$PR_REFS"
MARKERS_READ_OK=true
MARKERS="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments?per_page=100" --paginate --jq '.[] | select(.user.login == "github-actions[bot]" or .user.login == "qwen-code-ci-bot") | select(.body | startswith("<!-- qwen-triage on-hold sha=")) | .body' 2>/dev/null)" || MARKERS_READ_OK=false
if [ -n "$HEAD_SHA" ] && [ -n "$BASE_SHA" ] && [ "$MARKERS_READ_OK" = true ] && [[ "$MARKERS" == *"qwen-triage on-hold sha=${HEAD_SHA} base=${BASE_SHA}"* ]]; then
Comment thread
yiliang114 marked this conversation as resolved.
echo "Skipping automatic review: PR #${PR_NUMBER} carries status/on-hold pinned to head ${HEAD_SHA} and base ${BASE_SHA} (triage-only route). A maintainer can run @qwen-code /review explicitly." >> "$GITHUB_STEP_SUMMARY"
echo "should_run=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ -z "$HEAD_SHA" ] || [ -z "$BASE_SHA" ]; then
echo "status/on-hold present but the live head/base SHA was unreadable — failing open, running the review lane (#9193)." >> "$GITHUB_STEP_SUMMARY"
elif [ "$MARKERS_READ_OK" = false ]; then
echo "status/on-hold present but the marker comments were unreadable — failing open, running the review lane (#9193)." >> "$GITHUB_STEP_SUMMARY"
else
echo "status/on-hold present but not pinned to the live head/base (${HEAD_SHA}/${BASE_SHA}) — running the review lane (#9193)." >> "$GITHUB_STEP_SUMMARY"
# The stale pin means the triage-only exemption no longer
# matches the live diff, so strip the label too. It sits in
# stale.yml's exempt-pr-labels and Stage 1f applies it only
# once at open time — without this the PR would skip the
# review lane AND never age out via the stale bot.
if [ -n "$MARKERS" ] && gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels/status%2Fon-hold" -X DELETE 2>/dev/null; then
Comment thread
yiliang114 marked this conversation as resolved.
Comment thread
yiliang114 marked this conversation as resolved.
echo "Removed stale status/on-hold so the stale bot can age this PR (#9193)." >> "$GITHUB_STEP_SUMMARY"
# Invalidate the stale marker comment in the same pass:
# markers are upserted, never deleted, so leaving it in
# place keeps MARKERS non-empty forever and a maintainer
# who later re-applies status/on-hold to park the PR
# (documented workflow; the label is in stale.yml's
# exempt-pr-labels) would hit this stale-strip again
# instead of the manual-park carve-out above. Rewrite it
# with a body that no longer starts with the marker
# prefix; --update-only never mints a comment
# (#9193 review). A failed invalidation only warns: the
# worst case is one more strip of a re-applied label.
#
# GH_TOKEN must bind the MARKER AUTHOR's identity: the
# upsert lookup is author-scoped, the marker is posted by
# the triage agent under QWEN_CODE_BOT_TOKEN/CI_BOT_PAT,
# and this step's GITHUB_TOKEN resolves to
# github-actions[bot] — authenticating as anyone else
# makes --update-only a SILENT no-op (exit 0, no PATCH),
# leaving the marker live. Mirrors the sibling upsert
# sites' CI_BOT_PAT binding (docs-only relay/supersede).
invalidated_marker_file="$(mktemp "${RUNNER_TEMP:-/tmp}/qwen-triage-marker-invalidated.XXXXXX")"
printf '%s\n' "<!-- qwen-triage on-hold invalidated: stale pin stripped with the label at head ${HEAD_SHA} base ${BASE_SHA} (#9193). -->" > "$invalidated_marker_file"
if ! GH_TOKEN="${{ secrets.QWEN_CODE_BOT_TOKEN || secrets.CI_BOT_PAT }}" .github/scripts/upsert-bot-comment.sh "${GITHUB_REPOSITORY}" "${PR_NUMBER}" 'qwen-triage on-hold sha=' "$invalidated_marker_file" --update-only; then
Comment thread
yiliang114 marked this conversation as resolved.
echo "::warning::could not invalidate the stale triage marker comment (#9193)."
Comment thread
yiliang114 marked this conversation as resolved.
fi
rm -f "$invalidated_marker_file"
elif [ -z "$MARKERS" ]; then
echo "status/on-hold present without a triage marker — leaving the manual label in place (#9193)." >> "$GITHUB_STEP_SUMMARY"
else
echo "Failed to remove stale status/on-hold — leaving it in place (#9193)." >> "$GITHUB_STEP_SUMMARY"
fi
fi
fi
fi

if [ -n "$TRIGGER_COMMAND" ]; then
set -f
for token in $TRIGGER_COMMAND; do
Expand Down
Loading
Loading