mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-04 05:40:58 +00:00
* feat(triage): stop in-agent CI polling, finalize evidence and approval after CI completes
The triage agent's Stage 2b polled pending checks for up to 10 minutes, but
this repo's unit suite runs ~30 minutes, so the poll always burned its full
budget, gave up with 'CI still running', and Stage 3 could then approve before
the suite finished (observed on a PR approved 12 minutes before its Test job
completed).
Split the wait out of the agent entirely:
- pr-workflow.md Stage 2b now forbids polling: fetch check-runs once, report
pending checks honestly, and wrap the CI table in qwen-triage-ci region
markers keyed to the reviewed SHA.
- Stage 3 defers a clean-verdict approval when checks are still pending: the
comment carries an approve-on-green marker instead of an immediate APPROVE.
- New qwen-triage-finalize.yml fires on workflow_run completion of 'Qwen Code
CI' / 'E2E Tests' and, with plain bash over the API (no model, no checkout),
rewrites the marked table region with the settled results and posts the
commit-pinned approval only when every check landed green — failing closed
on red checks, a moved head, or a closed/draft PR, and flipping the triage
status comment to say which way it resolved.
Markers are honored only in comments authored by the bot identity itself, and
check names (attacker-influenced on fork PRs) go through the same HTML-escape
chain the skill mandates for file paths.
Stage comments now land ~10 minutes sooner and the approval, when deferred,
lands at CI completion with full evidence instead of before it.
* fix(triage): address finalize review — broken red gate, table truncation, dead trigger
Review findings on the finalize workflow, all reproduced before fixing:
- Blocker 1: the RED jq used the array-first membership form, where | rebinds
. and .conclusion indexes an array — jq exits 5 every run, RED comes back
empty, [ "" -gt 0 ] errors, and control falls through to the approve path:
a red CI auto-approved. The gate now binds the conclusion before the
membership test (IN(...)), and the counters are numeric-validated so any
future jq failure reads as 'cannot attest', never 'approve'.
- Blocker 2: the table rendered raw check-runs — on a real PR (96 runs, 35
names, 68 skipped) alphabetical sort + head -60 truncated away every actual
test job. table_rows now dedups per name (latest run), drops skipped rows,
and sorts running/non-green first so the cap can only cut green rows.
Replayed against the same PR: 96 rows -> 16, unit suite present.
- The approval gate now reads workflow runs filtered to event=pull_request
(deduped per workflow) instead of head-SHA check-runs, which also carry
long-running bot orchestration jobs that would wedge PENDING above zero at
the exact moment the last CI workflow fires — silently dropping the
deferred approval forever. The skill's Stage 3 PENDING count matches.
- E2E Tests had no pull_request trigger (dead entry); the workflows list is
now exactly the six pull_request-triggered workflows, so the last finisher
always re-fires the job.
- Head/state re-check moved before the red/deferred verdicts so a
cancel-in-progress firing on a stale SHA cannot stamp a red status over
the new head's comment; the still-deferred branch now updates the status
comment instead of staying invisible.
- replace_region fails closed when the end-marker text only precedes the
begin marker (awk END guard) — previously that shape truncated the comment
body, eating the signature and reviewed-commit footer.
- Region content is deterministic (no run URL) so the no-op cmp works;
empty run list or unavailable gate skips approval; comment wording fixed
(workflow_run jobs are attributed to the default branch, so the self-check
exclusion is belt-and-braces, not load-bearing).
Tests now execute the decision logic, not just grep for it: gate_counts and
table_rows run against fixtures covering every conclusion class, non-PR
events, re-run dedup, skipped filtering, ordering, and both marker-order
failure shapes. 30/30 passing.
* fix(triage): keep a stale finalize firing from clobbering the newer review's status comment
The status comment is deliberately not SHA-scoped (the triage workflow
creates it unscoped; scoping only the finalize side would orphan the
pairing), so a finalize firing for an old SHA that loses the race against a
newer head's green approval would overwrite the ✅ status with a stale
warning. Guard the stale path: when the current head already carries bot
sha= markers (a re-review owns the status comment), stay silent; when the
head moved with no re-review yet — triage does not auto-rerun on
synchronize — the stale note is accurate and still posts. Closed/draft PRs
now just log instead of flipping the status.
* fix(triage): close the guardrail bypass and align the finalize table with the gate
Second review round, all four findings reproduced or confirmed before fixing:
- The approve-on-green marker was emitted in Step 1 while the fork-refactor
GUARD only ran in Step 2 — a marker that slipped out on a fork refactor
would have been honored by the finalize job on green CI, bypassing the
guardrail entirely. GUARD now computes in Step 1 and gates the marker's
emission, and the finalize job re-asserts it structurally from the PR
state it already fetched (null head.repo = deleted fork = blocked), with
a 'guarded' status message instead of an approval.
- table_rows now restricts check-runs to the suites of the same deduped
event=pull_request workflow runs the gate trusts. Without it, 5 of 8
rendered rows on this PR's own head were bot plumbing presented as CI
evidence; with it, 115 raw check-runs reduce to exactly the 3 CI rows.
- A firing that saw PENDING>0 after the approval landed flipped the status
comment back to 'deferred' with nothing to ever right it; the
already-approved branch now repairs the status.
- Zero surviving table rows (failed runs fetch, missing suite ids) skips
the region rewrite instead of blanking the agent's table, and
replace_region refuses an empty region file (an unchecked getline would
have deleted the region and its markers unrecoverably).
Nits: the house github.repository guard on the job, the table header
matches the skill template, and the run-URL stays out of the region so the
no-op cmp keeps working.
|
||
|---|---|---|
| .. | ||
| ai-release-notes-workflow.test.js | ||
| build-and-publish-image-workflow.test.js | ||
| check-build-status.test.js | ||
| check-i18n.test.ts | ||
| chrome-extension-package.test.js | ||
| ci-flaky-rerun-workflow.test.js | ||
| ci-flaky-rerun.test.js | ||
| clean-package-build-artifacts.test.js | ||
| cli-entry.test.js | ||
| comment-attachment-guard-workflow.test.js | ||
| dev.test.js | ||
| generate-changelog.test.js | ||
| generate-release-notes.test.js | ||
| get-release-version-python-sdk.test.js | ||
| get-release-version.test.js | ||
| install-script.test.js | ||
| issue-triage-ownership-workflow.test.js | ||
| lint.test.js | ||
| main-ci-failure-issue-workflow.test.js | ||
| no-ak-integration-ci.test.js | ||
| package-assets.test.js | ||
| package-scripts.test.js | ||
| pr-force-push-reminder-workflow.test.js | ||
| pr-self-report-label.test.js | ||
| qwen-autofix-workflow.test.js | ||
| qwen-fleet-shepherd-workflow.test.js | ||
| qwen-pr-review-workflow.test.js | ||
| qwen-resolve-workflow.test.js | ||
| qwen-triage-finalize-workflow.test.js | ||
| qwen-triage-workflow.test.js | ||
| release-helpers.test.js | ||
| release-sdk-workflow.test.js | ||
| sandbox-command.test.js | ||
| sdk-node-exporter-stub.test.js | ||
| serve-fast-path-bundle-check.test.js | ||
| start.test.js | ||
| test-setup.ts | ||
| update-ecs-runner-qwen-workflow.test.js | ||
| upload-aliyun-oss-assets.test.js | ||
| vitest.config.ts | ||
| workspaces.test.js | ||