Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
4a4afb9
fix(ci): salvage superseded review runs and hold the loop's report-ti…
wenshao Aug 26, 2026
728227c
test(shepherd): classify the deferred stale-base headline as transient
wenshao Aug 26, 2026
f62091e
fix(ci): scope supersede-salvage per attempt and reap the watcher safely
wenshao Aug 26, 2026
29d6fd4
fix(ci): verify the live head before ceding and validate salvage signals
wenshao Aug 26, 2026
93cdb44
Merge remote-tracking branch 'origin/main' into review-hold-and-salvage
wenshao Aug 27, 2026
bde0965
fix(ci): make salvage signal IO FIFO-safe and cede decisions outcome-…
wenshao Aug 27, 2026
ef4ec86
fix(ci): cede genuinely superseded runs and restart the retry watcher
wenshao Aug 27, 2026
82c2ff1
Merge remote-tracking branch 'origin/main' into review-hold-and-salvage
qwen-code-dev-bot Aug 27, 2026
b16578f
fix(ci): close the salvage cede races and hostile-state aborts
qwen-code-dev-bot Aug 27, 2026
e2b0735
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Aug 28, 2026
3c94bfe
fix(ci): gate supersede cedes to automatic runs and dedup same-head r…
qwen-code-dev-bot Aug 28, 2026
8ab92c5
Merge branch 'main' into review-hold-and-salvage
wenshao Aug 28, 2026
31c186a
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Aug 28, 2026
c870613
fix(ci): bound salvage-signal reads and authenticate the delay-step d…
qwen-code-dev-bot Aug 28, 2026
ddbbbca
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Aug 28, 2026
44cce38
Merge branch 'main' into review-hold-and-salvage
wenshao Aug 28, 2026
b7162c8
fix(ci): cap the compose-latch read, poison-proof cleanups, and authe…
qwen-code-dev-bot Aug 29, 2026
1167d51
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Aug 29, 2026
2be14cd
Merge remote-tracking branch 'origin/main' into review-hold-and-salvage
qwen-code-dev-bot Aug 29, 2026
5f9da51
fix(ci): drop the forgeable delay-step dedup and scope the report hol…
qwen-code-dev-bot Aug 29, 2026
2106b94
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Aug 29, 2026
69df6d3
fix(ci): pin cede/salvage live-head reads to the real gh (#10123)
qwen-code-dev-bot Aug 30, 2026
f3b4f68
Merge remote-tracking branch 'origin/main' into review-hold-and-salvage
qwen-code-dev-bot Aug 30, 2026
c7c7ea3
fix(ci): pin supersede timestamp conversions to the real date, seal r…
qwen-code-dev-bot Aug 30, 2026
e7e73fa
fix(ci): merge main's /resolve rebuild, re-ratchet review workflow si…
qwen-code-dev-bot Aug 31, 2026
e0099c1
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Aug 31, 2026
8929f37
fix(ci): pin salvage clock/utility resolution and bound planted salva…
qwen-code-dev-bot Aug 31, 2026
437dd18
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Sep 1, 2026
326c07f
Merge remote-tracking branch 'origin/main' into review-hold-and-salvage
qwen-code-dev-bot Sep 2, 2026
c6df41a
fix(ci): pin the supersede watcher's utilities and bound future attem…
qwen-code-dev-bot Sep 2, 2026
f67f0ea
fix(ci): refuse bot-authored force-push events in the supersede rever…
qwen-code-dev-bot Sep 2, 2026
e68c22c
fix(ci): finish the review utility pin sweep and scope the cede kill …
qwen-code-dev-bot Sep 2, 2026
cde4c98
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Sep 2, 2026
1d02025
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Sep 3, 2026
5d9b65e
fix(ci): kill the superseded review attempt's process group, not just…
qwen-code-dev-bot Sep 3, 2026
7734e82
Merge branch 'main' into review-hold-and-salvage
qwen-code-dev-bot Sep 3, 2026
a9844e7
fix(ci): make the salvage replay suites truthful on macOS and pin the…
wenshao Sep 3, 2026
3b09316
Merge branch 'main' into review-hold-and-salvage
wenshao Sep 3, 2026
e4602fa
Merge branch 'main' into review-hold-and-salvage
wenshao Sep 3, 2026
e76afd4
fix(ci): neutralize inherited review-lane captures in the replay harn…
wenshao Sep 4, 2026
527910c
fix(ci): keep the deadline-epoch line bare — packages/cli pins it as …
wenshao Sep 4, 2026
a4b54e5
docs(ci): record why the revert witness excludes only the agent's cre…
wenshao Sep 4, 2026
201c4d1
fix(ci): make the supersede watcher's TERM->KILL wind-down immune to …
wenshao Sep 4, 2026
19b6ead
Merge branch 'main' into review-hold-and-salvage
wenshao Sep 4, 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
4 changes: 2 additions & 2 deletions .github/workflows/.size-baseline
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
6495 pr-self-report-label.yml
9646 qwen-autofix-fork-bridge.yml
5942 qwen-autofix-fork-signal.yml
397656 qwen-autofix.yml
403688 qwen-autofix.yml
7061 qwen-ci-flaky-rerun.yml
158010 qwen-code-pr-review.yml
173247 qwen-code-pr-review.yml
79041 qwen-fleet-shepherd.yml
20525 qwen-issue-followup-bot.yml
5760 qwen-pr-safety-precheck.yml
Expand Down
53 changes: 50 additions & 3 deletions .github/workflows/qwen-autofix.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ task-oriented guides — what a maintainer types and what happens next — see:
- [145. review-address · Report dry-run / failure — CUMULATIVE timeout breaker — the sibling of the consecutive one above, for the…](#af-145)
- [146. review-address · Report dry-run / failure — The agent committed (verify recorded committed=true before any gate could fail),…](#af-146)
- [147. review-address · Report dry-run / failure — Same byte-budget hygiene as the English excerpt above. 3000 bytes ≈ 1000 CJK…](#af-147)
- [148. review-address · Report dry-run / failure — Hold the stale-base refresh while a review-pr is in flight on the PR.…](#af-148)

---

Expand Down Expand Up @@ -2764,10 +2765,14 @@ In `review-scan` · `Scan for PRs with new feedback`.
```text
Delay-window fallback: a review run parked BEFORE its job
starts (the 10-minute environment wait) has no review-pr
check-run yet, but a push now would still cancel it via
synchronize. Only pull_request_target runs are cancelable —
check-run yet, but a push now would still supersede it (the
review workflow's own supersede handling, #10110: a parked or
pre-threshold run yields to the push — its work is discarded
exactly as the old synchronize cancel did — and only a
past-threshold run salvages; the hold keeps the loop from
forcing either). Only pull_request_target runs are affected —
comment/review-triggered runs use per-run concurrency groups
that a synchronize never cancels, so holding the round for
that a synchronize never touches, so holding the round for
one would defer autofix for nothing (R2-1). Match against the
scan's REVIEW_RUNS_JSON fetch — one page of the review
workflow's runs, empty on lookup failure — by immutable head
Expand Down Expand Up @@ -3727,3 +3732,45 @@ forbids HTML in failure.zh.md), but must not be able to open
or close a <details>/<summary> that swallows the closing tag
the workflow emits below.
```

<a id="af-148"></a>

### 148. review-address · Report dry-run / failure — Hold the stale-base refresh while a review-pr is in flight on the PR.

In `review-address` · `Report dry-run / failure`.

```text
The scan's dispatch gate (#8888/#8899) already refuses to start a round
while review-pr is live, but the loop had one more head-moving write
outside that hold: this step's stale-base retry calls update-branch at
REPORT time, hours after the dispatch gate last looked. A review can
start in that window — a human /review comment, a bot re-request, or a
run the scan's fail-open probe missed — and the merge push would then
supersede a lifecycle review run mid-flight (#10110; before the salvage
threshold that discards its work exactly as the old cancel did), or
invalidate a command run's posting: every review pins the head it
reviews (QWEN_CI_REVIEW_EXPECTED_HEAD_SHA) and its guard blocks the
final post when the head moved, so even the uncancellable per-run-group
reviews lose their whole run to a head move.

So the retry probes for a live review first, with the scan gate's exact
probe pair: the statusCheckRollup filter (any live review-pr check from
the review workflow, trigger-independent), then the runs-API fallback
for pull_request_target runs still parked in the 10-minute delay window
with no check-run yet. On a live review the update is DEFERRED, not
skipped: the same 9999 sentinel MARK_TS the retry branch uses keeps the
feedback live, the next scan re-runs the round (itself held while the
review is still in flight), and that round's report step performs the
refresh once the review has landed. One extra round of latency, bounded
by MAX_ROUNDS, against hours of discarded review work.

Fail-open on probe errors, deliberately: the probe is an optimization,
and failing closed would wedge stale-base recovery — the path that
un-sticks red PRs — on any transient API error. A probe error therefore
reads as "no review live" and the update proceeds, which is exactly the
pre-#10110 behavior. The deferred headline joins CONSEC_FAIL's
streak-reset needles ("deferred a stale-base refresh"): like the
updated-a-stale-base round it defers to, the round's failure is not
evidence about the PR, and counting it toward the cap would park a PR
for having been reviewed at the wrong moment.
```
49 changes: 42 additions & 7 deletions .github/workflows/qwen-autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2568,8 +2568,9 @@ jobs:
if [[ "${REVIEW_PR_LIVE}" != "true" && -n "${REVIEW_WF_ID}" && -n "${PR_HEAD_OID}" ]]; then
# Delay-window fallback: a review run parked BEFORE its job
# starts (the 10-minute environment wait) has no review-pr
# check-run yet, but a push now would still cancel it via
# synchronize.
# check-run yet, but a push now would still supersede it
# (#10110: a parked or pre-threshold run yields to the push —
# its work is discarded exactly as a cancel did).
# Full rationale → qwen-autofix.md#af-099
REVIEW_RUN_STARTED_AT="$(jq -r --arg wf "${REVIEW_WF_ID}" --arg pr "${PR}" --arg head "${PR_HEAD_OID}" '
[ .workflow_runs[]?
Expand Down Expand Up @@ -5684,13 +5685,40 @@ jobs:
# generated checks complete after the conflict marker this
# same report posts — waking the very park it establishes.
# Full rationale → qwen-autofix.md#af-143
STALE_BASE_DEFERRED=false
if [[ "${AUDIT_VERDICT:-}" != 'conflict' ]]; then
MAIN_HEAD_R="$(gh api "repos/${REPO}/commits/${DEFAULT_BRANCH:-main}" --jq '.sha' 2> /dev/null || echo '')"
if [[ -n "${MAIN_HEAD_R}" && -n "${REPORT_HEAD}" ]]; then
CMP_R="$(gh api "repos/${REPO}/compare/${MAIN_HEAD_R}...${REPORT_HEAD}" --jq '.status' 2> /dev/null || echo '')"
if [[ "${CMP_R}" == 'behind' || "${CMP_R}" == 'diverged' ]] \
&& gh api -X PUT "repos/${REPO}/pulls/${PR}/update-branch" -f expected_head_sha="${REPORT_HEAD}" > /dev/null 2>&1; then
STALE_BASE_RETRY=true
if [[ "${CMP_R}" == 'behind' || "${CMP_R}" == 'diverged' ]]; then
# Review-in-flight hold for the loop's OWN report-time
# head move (#10110): same probe pair as the scan's
# dispatch gate (#8888), fail-open on probe errors.
# Full rationale → qwen-autofix.md#af-148
ROLLUP_R="$(gh pr view "${PR}" --repo "${REPO}" --json statusCheckRollup --jq '.statusCheckRollup // []' 2> /dev/null || echo '[]')"
REVIEW_LIVE_R="$(jq -r '
[ .[]
| select((((.status // .state // "") | IN("QUEUED", "IN_PROGRESS", "PENDING", "WAITING", "REQUESTED")) and ((.name // "") == "review-pr") and ((.workflowName // "") == "🧐 Qwen Pull Request Review"))) ]
| length > 0
' <<< "${ROLLUP_R}" 2> /dev/null || echo '')"
if [[ "${REVIEW_LIVE_R}" != 'true' ]]; then
RWID_R="$(gh api "repos/${REPO}/actions/workflows/qwen-code-pr-review.yml" --jq '.id' 2> /dev/null || echo '')"
LIVE_HEAD_R="$(jq -r '.headRefOid // ""' <<< "$(gh pr view "${PR}" --repo "${REPO}" --json headRefOid 2> /dev/null || echo '{}')")"
if [[ -n "${RWID_R}" ]]; then
REVIEW_LIVE_R="$(gh api "repos/${REPO}/actions/workflows/${RWID_R}/runs?per_page=100" 2> /dev/null | jq -r --arg pr "${PR}" --arg head "${LIVE_HEAD_R}" '
[ .workflow_runs[]?
| select((.event // "") == "pull_request_target")
| select((.status // "") | IN("queued", "waiting", "pending", "requested", "in_progress"))
| select((($head != "") and ((.head_sha // "") == $head)) or any(.pull_requests[]?; (.number | tostring) == $pr)) ]
| length > 0
' 2> /dev/null || echo '')"
fi
fi
if [[ "${REVIEW_LIVE_R}" == 'true' ]]; then
STALE_BASE_DEFERRED=true
elif gh api -X PUT "repos/${REPO}/pulls/${PR}/update-branch" -f expected_head_sha="${REPORT_HEAD}" > /dev/null 2>&1; then
STALE_BASE_RETRY=true
fi
fi
fi
fi
Expand All @@ -5702,6 +5730,13 @@ jobs:
MARK_TS='9999-12-31T23:59:59Z'
HEADLINE="🤖 AutoFix updated a stale base — the fix did not pass verification, but this PR was behind \`${DEFAULT_BRANCH:-main}\`, so it merged current main in via update-branch and will retry on the next scan. A stale base (a dependency or symbol main already changed) can fail the build without being the fix's fault; if it still fails once current, it hands off to a human."
HEADLINE_ZH="🤖 AutoFix 更新了一个过期的 base —— 修复未通过验证,但本 PR 落后于 \`${DEFAULT_BRANCH:-main}\`,因此已通过 update-branch 合入当前 main,并将在下次扫描时重试。过期的 base(main 已改动的依赖或符号)可能让构建失败而并非修复本身的错;若 base 更新后仍然失败,将移交人工处理。"
elif [[ "${STALE_BASE_DEFERRED}" == 'true' ]]; then
# Same sentinel semantics as the retry branch: feedback stays
# live, the next round re-runs, and ITS report step performs
# the base refresh once the review has landed.
MARK_TS='9999-12-31T23:59:59Z'
HEADLINE="🤖 AutoFix deferred a stale-base refresh — the fix did not pass verification and this PR is behind \`${DEFAULT_BRANCH:-main}\`, but a review of this PR is still in flight, and merging main now would cancel it or invalidate its posting (#10110). The refresh and this item's retry move to the next scan."
Comment thread
wenshao marked this conversation as resolved.
Outdated
HEADLINE_ZH="🤖 AutoFix 暂缓了一次过期 base 刷新 —— 修复未通过验证且本 PR 落后于 \`${DEFAULT_BRANCH:-main}\`,但该 PR 上仍有一轮评审在运行,现在合入 main 会取消该评审或使其发布失效(#10110)。base 刷新与此项的重试推迟到下次扫描。"
else
# Say what actually happens next. The old "A human should
# take over this PR" read as a full release, but the loop
Expand Down Expand Up @@ -5758,7 +5793,7 @@ jobs:

# Consecutive-failure circuit breaker, distinct from the round cap.
# Full rationale → qwen-autofix.md#af-069
if [[ "${MARK_ROUND}" != "${MAX_ROUNDS}" ]] && [[ "${PREPARE_OUTCOME}" == 'success' || "${PREPARE_OUTCOME}" == 'failure' ]] && [[ "${STALE_BASE_RETRY:-false}" != 'true' ]] && { [[ -z "${API_ERROR_DETAIL}" ]] || [[ "${API_ERROR_KIND}" == 'auth' ]]; }; then
if [[ "${MARK_ROUND}" != "${MAX_ROUNDS}" ]] && [[ "${PREPARE_OUTCOME}" == 'success' || "${PREPARE_OUTCOME}" == 'failure' ]] && [[ "${STALE_BASE_RETRY:-false}" != 'true' ]] && [[ "${STALE_BASE_DEFERRED:-false}" != 'true' ]] && { [[ -z "${API_ERROR_DETAIL}" ]] || [[ "${API_ERROR_KIND}" == 'auth' ]]; }; then
CONSEC_FAIL=1
if [[ -f "${WORKDIR}/ic.json" ]]; then
COMMENTS_JSON="$(cat "${WORKDIR}/ic.json")"
Expand All @@ -5780,7 +5815,7 @@ jobs:
| (.body | gsub("\r"; "") | split("\n")[0])' <<< "${COMMENTS_JSON}" 2> /dev/null || true)"
while IFS= read -r H; do
[[ -n "${H}" ]] || continue
if [[ "${H}" == *"Addressed the latest review feedback"* || "${H}" == *"no changes needed"* || "${H}" == *"AutoFix could not start —"* || "${H}" == *"updated a stale base"* ]]; then
if [[ "${H}" == *"Addressed the latest review feedback"* || "${H}" == *"no changes needed"* || "${H}" == *"AutoFix could not start —"* || "${H}" == *"updated a stale base"* || "${H}" == *"deferred a stale-base refresh"* ]]; then
CONSEC_FAIL=1
else
CONSEC_FAIL=$(( CONSEC_FAIL + 1 ))
Expand Down
Loading
Loading