qwen-code/scripts
Shaojin Wen 6acfc9a22f
feat(autofix): label-driven takeover and release; fix forced-dispatch green no-op (#7165)
* feat(autofix): label-driven takeover and release; fix forced-dispatch green no-op

Takeover, exactly as designed: applying autofix/takeover (GitHub
triage+ only — the permission gate is GitHub's own) summons the loop
onto a PR, human-authored included; removing it releases the PR. The
autofix/skip label opts any PR out at every engagement path — the
autofix scan, the forced path, and the Fleet Shepherd walk — and wins
when both labels are present. Every toggle gets a bilingual ack from
the PAT-verified bot identity. The comment-command surface stays
CLOSED: the pinned no-comment-commands contract test is untouched.

Plumbing: pull_request labeled/unlabeled triggers; label events share
the per-PR route group (the event class is triage-gated); the group
expression also carries the #7163 payload trust prefilter so the two
open PRs converge on the same final text in either merge order; scan
candidates become bot PRs ∪ takeover PRs minus skip PRs (deduped);
fork label events are logged and dropped (no secrets to even post a
rejection).

The forced-validation replay immediately caught a latent bug live
since #6528: '(.isCrossRepository // true) | not' is false for EVERY
input (jq's // treats false as empty), so every workflow_dispatch
pr_number=N run — including the shepherd's conflict dispatches —
validated to targets=[] and exited GREEN having done nothing. Fixed to
'.isCrossRepository == false' (fail-closed on a missing field) with a
replay case pinning the trap.

Tests: autofix 55/55 (label engage/release pins, candidate-selection
replay with skip-wins and fork cases, forced-validation replay across
author/takeover/skip/closed/fork/missing-field); shepherd 12/12
(skip-filter replay).

* feat(autofix): '@qwen-code /takeover' comment sugar over the takeover label

Maintainer-mandated reopening of the comment surface, in the narrowest
possible form: '@qwen-code /takeover' applies TAKEOVER_LABEL and
'@qwen-code /takeover stop' removes it — nothing else. The label stays
the single source of truth: engagement and release happen exclusively
via the pull_request label events, so a manual label edit and the
command are one mechanism with two entry points, and the command's
whole blast radius is one label toggle.

Gates: an expression-level startsWith prefilter keeps ordinary
comments from ever starting a job; the body must match the constant
EXACTLY after trimming (no parsing, no arguments); allowed senders are
the PR author (who may lack label access — this is who the sugar is
FOR) or a write+ collaborator via the same permission API used by
review routing; closed PRs, non-PR comments, and the bot itself are
ignored. The toggle job is PAT-verified and presence-aware (no-op
toggles are explicit, since they fire no label event).

The 'does not expose comment-triggered autofix commands' contract test
is REWRITTEN into pinning this gated design, documenting the deliberate
reversal. New behavioral replay drives the extracted command branch
with a PATH-stubbed permission API across eight author/write/read/
exact-match/non-PR/closed/self cases. 56/56.

* feat(autofix): raise the round cap to 50 while a PR is under takeover

Large managed PRs routinely need dozens of feedback rounds — that is
the point of takeover — so the unattended MAX_ROUNDS=5 would strangle
exactly the PRs the label exists for. While TAKEOVER_LABEL is present
the effective cap is TAKEOVER_MAX_ROUNDS=50: the circuit breaker stays
(a bot/review-bot ping-pong is still bounded and every round still
requires new trusted feedback or a conflict), it is just sized for
explicitly delegated work. Removing the label restores the strict cap
on the next scan.

The scan computes the effective cap from the candidate's live labels
and stamps it into the matrix target; the address job shadows the
workflow-level MAX_ROUNDS with the matrix value, so every round
message, marker, and cap gate uses the same number consistently
(including #7163's address-time cap discard once both merge).

57/57 with a verbatim cap-selection replay (labeled → 50, plain → 5).

* feat(autofix): re-armable round windows, cap raised to 100, visible cap pause

The round counter is DERIVED state, stored nowhere but in the bot's
eval-marker comments on the PR — and counting is now windowed by the
latest '<!-- takeover-ack engaged -->' comment. Re-engaging (label
off→on, or repeating the takeover command on an already-managed PR,
which now posts a re-arm ack instead of a silent no-op) starts a fresh
window: a PR that exhausted its rounds continues under management with
one human action, auditable in the PR timeline. The WATERMARK stays
global across windows — feedback already addressed is never replayed —
and a PR never taken over has no ack, so strict lifetime counting is
unchanged. The prepare-side live round is windowed identically, so
pre-reset markers can neither trip the cap nor look like same-ts
round-advance duplicates (replay-proven).

TAKEOVER_MAX_ROUNDS rises to 100 per maintainer sizing, and pausing at
the cap is now VISIBLE on managed PRs: a bilingual notice with re-arm
guidance, once per counting window (marker-deduped past the latest
re-arm; a failed post retries next scan).

58/58: rearm windowing replay (no ack → lifetime; ack → round 0 with
watermark preserved; new rounds count from 1; latest ack wins), the
stale-gate re-arm interplay case, cap-selection at 100, and cap-notice
dedup pins.

* feat(autofix): collapsed-Chinese bilingual takeover comments; fix ESLint regex-spaces

Every takeover-flow comment — engage ack, release ack, re-arm ack, and
the cap-pause notice — now follows the project convention: English
body plus Chinese collapsed under <details><summary>中文说明</summary>
(pinned at exactly four sites). Bodies are built via printf so no
workflow indentation leaks into the markdown: the previous literal
multi-line strings embedded 10 leading spaces, which would have
rendered the trailing marker comment as a visible code block.

Also fixes the CI failure at 1497a7e6f: three extraction regexes used
literal space runs, tripping ESLint no-regex-spaces — now {n}
quantifiers. Lesson applied: prettier alone is not the lint gate.

58/58 + 12/12.

* fix(autofix): fork-safe takeover release, verified cap-notice write, doc drift

Review round (issue comment on head c2d8a89), all three findings:

- F1 (bug): 'unlabeled' on a fork PR emitted the release ack
  unconditionally; fork pull_request runs carry no secrets, so
  takeover-ack failed its PAT identity check — a red run, reachable
  self-serve (the comment sugar accepts the fork PR's author, sticking
  the label; any later unlabel fired the red run). The unlabeled
  branch now mirrors the fork log-and-drop, and takeover-command —
  which runs in issue_comment context WITH secrets — refuses fork PRs
  up front with a bilingual explanation (adoption guidance), so the
  label can no longer stick to forks via the command at all.
- F2 (convention): the scan's cap notice now verifies the PAT
  identity before writing (memoized per run): a rotated PAT would
  post under a foreign login that the AUTOFIX_BOT-scoped dedup can
  never see, reposting every scan. Scan header updated — its single
  write is identity-verified.
- F3 (doc drift): two leftover '50' references updated to speak of
  TAKEOVER_MAX_ROUNDS.
- Note adopted: the leading-whitespace prefilter/trim nuance is now
  documented at the prefilter.

58/58 + 12/12; collapsed-Chinese sites now pinned at five (the
fork-refusal joins the four acks).

* fix(autofix): window-keyed rounds, ordered commands, skip closure, marker scrub

Second review round on the takeover feature (dev-bot GPT-5 review at
c2d8a89) — all seven Criticals and three Suggestions:

- Round windows are now keyed, not timestamped: every eval marker
  records the window key it was produced under (win=…, legacy counts
  as 'none'), the current key is the latest engage ack's created_at,
  and only current-key markers count toward the cap. An in-flight
  address job selected before a re-arm can no longer re-cap the fresh
  window with a late marker — and prepare discards any job whose key a
  re-arm superseded while it sat queued (conflicts stay actionable).
- Takeover commands are serialized per PR (queued concurrency group):
  an older /takeover cannot land after a newer /takeover stop read the
  unlabeled state.
- Skip closure at every remaining gap: the command refuses engage and
  re-arm on a skip-labeled PR (bilingual skip-blocked ack, no bogus
  window anchor); the label-event ack does the same; the scan's fresh
  per-PR fetch re-checks skip mid-scan; the shepherd re-checks the
  LIVE label immediately before its two mutating levers (fail closed —
  unreadable labels count as skipped).
- Honest release on bot-authored PRs: removing takeover now says
  standard bot management continues (strict cap); only takeover mode
  ends.
- Model files posted verbatim as PR comments (address-summary,
  no-action) are scrubbed of HTML comments — the rule the handoff
  DETAIL already applied — so induced output cannot forge control
  markers the scanners would trust.
- Test hardening per the Suggestions: every bilingual body is asserted
  individually (8 printf bodies), and the gh pr list producers must
  request 'labels' in both workflows (fixture-driven consumers alone
  went green without it).

58/58 + 12/12; the rearm replay now proves the race the key model
closes (an old-window round-50 marker landing after the ack counts 0).

* fix(autofix): sever PR hooks from PAT pushes; scope label routing; fork-author command gate

Third review round (yiliang114 P0-P3 + a second inline batch):

- P0: the address/publish push steps carry the PAT while the branch
  carries PR-controlled .husky hooks (hooksPath was pointed there so
  the agent's commits get checked) — a pre-push hook would execute
  that code with the PAT in env. Both push sites now sever hooks
  (core.hooksPath=/dev/null + git push --no-verify). The force-push
  guard regex also false-positived on --no-verify ('-…f') and is now
  precise (-f word / +refspec).
- P1: only the takeover label itself shares the per-PR route group;
  an unrelated label changed in the same batch operation can no
  longer cancel a queued takeover route.
- P2: candidate INSPECTION is bounded (MAX_CANDIDATE_INSPECTIONS=60),
  not just emitted targets — idle candidates burn serial API calls
  and takeover widens the pool. Excess rotates to the next scan.
- Fork-author command gate: author privilege is in-repo only — a
  fork-PR author can no longer summon even PAT-authored refusal
  comments onto their own PR (silent drop); write+ maintainers still
  reach the explanatory fork refusal. Replay-proven both ways.
- Candidates jq now fails closed on the fork field (== false),
  matching the forced path and the //-trap NOTE.
- The unlabeled path mirrors the labeled-path state guards: releasing
  a closed/non-main/fork PR acks nothing (it was never engaged).
- Command-contract docs updated to the real side-effect set, and the
  four-path toggle (plus skip/fork refusals) now has a full
  gh-recording behavioral replay.

59/59 + 12/12.

* fix(autofix): live cap-notice dedup key, line-proof marker scrub, checkout hook severing

Round-3 review (issue comment at 427301c64), all four findings:

- R1 (regression): the window-key rename left the cap-notice dedup on
  a dangling REARM_TS — empty rt made every historical notice count,
  silently turning per-window dedup into per-lifetime, so a re-armed
  PR hitting the cap again paused with no reminder (the feature's
  headline case). Now NOTICE_RT=REARM_KEY with the 'none' corner
  falling back to lifetime dedup (created_at > 'none' is never true
  lexically, which would have flipped it into posting every scan).
  The extracted-jq replay proves all three regimes.
- R2: the HTML-comment strip was line-oriented while jq scan()
  matches across newlines — a marker split over two lines survived
  the scrub and still parsed. All three publish sites now neutralize
  the opening token itself (sed 's/<!--/<!\-\-/g' — line-independent,
  and the backslashes render away in markdown). Proven end-to-end on
  a split forged marker, with the sed extracted verbatim.
- R3 (pre-existing, folded in as the natural home): prepare's
  checkout -B ran PR-controlled post-checkout hooks with the PAT in
  env. Prepare now severs hooks like the push steps; the agent step —
  no PAT, sandboxed tools — re-points .husky itself so its commits
  still get checked.
- R4: candidates now drain NEWEST-first (unique_by sorts ascending;
  sort_by(-.number) added), the comment states the real semantics
  instead of a rotation that never existed, and the free busy skip no
  longer consumes inspection budget.

59/59 + 12/12.

* fix(autofix): takeover-ack state read fails closed

Review suggestion, adopted verbatim: the ack's gh pr view fell back to
'{}' on failure, defaulting HAS_SKIP to false — a transient API
failure could post a wrong 'engaged' ack on a skip-labeled PR. It now
exits like the sibling takeover-command job (a red ack job posts
nothing; engagement is scan-driven and unaffected). Pinned both ways.

* fix(autofix): command-style comments are instructions, not review feedback

Motivating case: a maintainer posted '@qwen-code /triage' on a bot PR
and the loop burned a full agent cycle (checkout, install, build,
agent) to publish a no-action report explaining that the command is
not feedback. The takeover command introduced here would self-trigger
the same round on every engagement.

Comments matching '^\s*@qwen-code /' (any author) are now excluded at
all four feedback-decision sites — the scan's issue-comment count, the
NEWEST computation, the LIVE_NEW recount, and the prompt renderer —
alongside the existing bot-marker filter. Behaviorally proven: a
trusted takeover-command comment newer than the live watermark no
longer rescues a stale duplicate into an agent round, and the raw jq
counts real feedback while dropping the command. 59/59 + 12/12.

* fix(shepherd): fork check fails closed, matching the autofix convention

Review suggestion, adopted verbatim: the fleet filter kept
'.isCrossRepository != true' (fail-open on a missing field) while the
autofix candidates migrated to '== false' in the same PR. Aligned;
the filter replay now includes a missing-field row and proves it is
rejected. 12/12.

* fix(autofix): trusted runner staging, live author privilege, rotation, sentinel watermark

Auto-review of the merged head (16 findings; 11 adopted, 3 rebutted in
replies, 1 rename, 1 already-covered):

- The address agent step invoked run-agent.mjs from the CHECKED-OUT PR
  branch with the model key in env — branch-controlled code on the
  host (takeover targets human branches). The runner is now staged
  from the trusted base into RUNNER_TEMP (same pattern as the schema
  gate) and invoked from there.
- Author command privilege is LIVE, not durable: authors must hold
  triage+ today, so an ex-member's authorship no longer summons
  secret-bearing runs (the sugar's audience — members below write —
  still qualifies).
- The terminal-handoff sentinel ts is excluded from watermark
  computation (scan and prepare): it is a flag, not an evaluation
  time, and it previously made a re-arm after a terminal handoff dead
  on arrival by filtering all future feedback forever. Terminal
  skipping stays round-based and thus window-scoped.
- Candidate inspection gains a rotating start offset (fixed
  newest-first plus the budget starved the oldest tail FOREVER once
  the pool exceeded the budget).
- issue_comment events get their own per-PR command group: bursts
  coalesce away from review routes, and pending-slot replacement is
  exactly latest-intent.
- The queued toggle re-verifies OPEN + base=main; the cap notice
  honors dry-run and re-verifies live consent before posting; the
  release ack on a skip-labeled bot PR now says skip governs.
- Shepherd: live_skip is reason-aware (an API outage is reported as
  fail-closed, never as consent withdrawn) and both levers check their
  budgets BEFORE the PAT-backed live read.
- Tests: the supersede fixture is now discriminating (old-window
  marker lands AFTER the ack — timestamp-windowing would have counted
  it); the force-push guard catches combined short options (-uf); the
  sentinel-watermark rule and rotation are replayed; the labels test
  is renamed to stop claiming the comment surface is closed.

60/60 + 12/12.

* test(autofix): restore recheck-before-checkout ordering pin; pin all win-marker sites

Two review suggestions, both fallout from the merge-resolution test
unification: the eligibility recheck's BEFORE-checkout ordering
assertion (lost with the replaced pin test) is back inside the replay
test, and all three eval-marker producers now carry per-site win=
format pins (the global count-3 catches removal but not a
lose-one-gain-a-duplicate swap). 60/60.

* fix(autofix): trusted-commenter cmd groups; hooks severed at every host checkout; honest model-key framing

- The issue_comment command group now requires a trusted-looking
  payload association (same prefilter pattern as reviews): an
  arbitrary commenter's rejected command can no longer cancel a
  maintainer's queued command out of the shared per-PR group.
- Both verification checkouts (review gate and issue publish) sever
  hooks like every other host checkout — no secret sits in those
  steps, but branch post-checkout code on the host broke the
  convention (five severing sites now pinned).
- The agent-step comment and the PR risk section now state the model
  key's real exposure explicitly: the CLI forwards OPENAI_API_KEY
  into the sandbox and the agent's job is to build/test the branch,
  so a taken-over branch's scripts can read AUTOFIX_OPENAI_API_KEY —
  an accepted, documented consequence of takeover (PAT remains fully
  severed via trusted staging + hook severing); the key should be
  low-privilege and rotatable. 60/60 + 12/12.

* fix(autofix): label events get their own route group; non-takeover labels never start jobs

Two review suggestions, adopted verbatim: pull_request label events now
group as route-label-{N} (distinct from the review group — a
simultaneous review and label toggle on the same PR can no longer
cancel each other), and the route job gate filters pull_request events
to the takeover label, so a triage labeling session across dozens of
PRs burns zero runner slots. 60/60.

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-19 07:35:13 +00:00
..
installation fix(packaging): bundle clipboard addon in standalone builds (#6708) 2026-07-11 15:18:24 +00:00
lib feat(core): support QWEN_HOME env var to customize config directory (#2953) 2026-05-09 15:51:52 +08:00
tests feat(autofix): label-driven takeover and release; fix forced-dispatch green no-op (#7165) 2026-07-19 07:35:13 +00:00
acp-http-smoke.mjs feat(daemon): merge daemon-mode feature batch into main (#4490) 2026-06-12 00:34:49 +08:00
benchmark-api-latency.mjs feat(cli): add API preconnect to reduce first-call latency (#3318) 2026-04-27 06:54:55 +08:00
build-hosted-installation-assets.js fix(installer): auto-detect SYSTEM account and default PATH scope to machine (#4903) 2026-06-10 21:02:10 +08:00
build-standalone-release.js fix(packaging): bundle clipboard addon in standalone builds (#6708) 2026-07-11 15:18:24 +00:00
build.js feat(channels): add WeCom intelligent robot channel (#6436) 2026-07-07 15:24:19 +00:00
build_package.js fix(build): clean stale outputs before tsc --build to prevent TS5055 (#4453) 2026-05-23 23:06:31 +08:00
build_sandbox.js fix(sandbox): fall back to 'latest' tag when image name has no colon (#2962) 2026-04-18 09:07:05 +08:00
build_vscode_companion.js Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
check-build-status.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
check-desktop-isolation.js feat(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) 2026-06-11 21:57:20 +08:00
check-i18n.ts fix(cli): localize approval mode UI labels (#6592) 2026-07-11 00:07:03 +00:00
check-lockfile.js Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
check-serve-fast-path-bundle.js perf(cli): Defer TUI runtime from ACP startup (#7182) 2026-07-19 00:10:15 +00:00
clean-package-build-artifacts.js test(core): stabilize file history eviction test (#6637) 2026-07-10 06:39:52 +00:00
clean.js feat(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) 2026-06-11 21:57:20 +08:00
cli-entry.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
copy_bundle_assets.js feat(core): add dataviz bundled skill (#6198) 2026-07-03 01:06:39 +00:00
copy_files.js revert: remove unused script modifications 2026-02-10 14:34:36 +08:00
create-standalone-package.js fix(cli): avoid updating active CLI processes (#6874) 2026-07-15 00:33:17 +00:00
create_alias.sh fix: ambiguous literals (#461) 2025-08-27 15:23:21 +08:00
daemon-dev.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
desktop-openwork-sync.ts feat(acp): support desktop qwen integration (#4728) 2026-06-09 19:09:44 +08:00
dev.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
esbuild-shims.js perf(cli): code-split lowlight to cut startup V8 parse cost (#4070) 2026-05-15 17:26:18 +08:00
generate-changelog.js feat(release): generate AI-assisted release notes (#6756) 2026-07-12 13:00:22 +00:00
generate-git-commit-info.js # 🚀 Sync Gemini CLI v0.2.1 - Major Feature Update (#483) 2025-09-01 14:48:55 +08:00
generate-release-notes.js feat(release): generate AI-assisted release notes (#6756) 2026-07-12 13:00:22 +00:00
generate-settings-schema.ts revert: remove local PR verification gate (#7031) 2026-07-16 11:24:38 +00:00
get-release-version.js fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476) (#6481) 2026-07-08 12:21:14 +00:00
lint.js revert: remove local PR verification gate (#7031) 2026-07-16 11:24:38 +00:00
local_telemetry.js Merge tag 'v0.3.0' into chore/sync-gemini-cli-v0.3.0 2025-09-11 16:26:56 +08:00
measure-flicker.mjs fix(cli): bound SubAgent display by visual height to prevent flicker (#3721) 2026-04-29 22:34:55 +08:00
pre-commit.js Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
prepare-package.js fix(release): raise prepared package size limit to 96 MB (#6687) (#6691) 2026-07-11 01:01:59 +00:00
prepare.js feat(web-shell): git status chip, visual working-tree diff, and sidebar git status (#7054) 2026-07-18 10:06:07 +00:00
release-script-utils.js feat(installer): add standalone hosted install and uninstall flow (#3828) 2026-05-21 11:57:10 +08:00
sandbox_command.js fix(scripts): avoid shell injection in sandbox command detection (#6108) 2026-07-01 16:20:40 +08:00
sign-release.sh feat(cli): add standalone auto-update support (#4629) 2026-06-04 22:53:12 +08:00
start.js fix(review): report what the transcripts prove; build the roster in one call (#7033) 2026-07-18 00:43:57 +00:00
sync-computer-use-schemas.ts feat(computer-use): configurable screenshot max dimension (setting + env) (#5122) 2026-06-15 15:25:27 +08:00
telemetry.js feat(core): support QWEN_HOME env var to customize config directory (#2953) 2026-05-09 15:51:52 +08:00
telemetry_gcp.js fix(mcp): update OAuth client names and improve MCP commands 2026-02-08 10:46:48 +08:00
telemetry_utils.js feat(core): support QWEN_HOME env var to customize config directory (#2953) 2026-05-09 15:51:52 +08:00
test-rewind-e2e.sh fix(test): update rewind E2E Test 1 assertion after isRealUserTurn fix (#3622) 2026-04-26 06:49:42 +08:00
test-windows-paths.js chore: consistently import node modules with prefix (#3013) 2025-08-25 20:11:27 +00:00
unused-keys-only-in-locales.json feat: add /diff command and git diff statistics utility (#3491) 2026-05-10 11:15:59 +08:00
upload-aliyun-oss-assets.js fix(release): move constants above entry point to avoid TDZ error (#4398) 2026-05-23 22:21:33 +08:00
verify-installation-release.js feat(installer): verify release assets + switch public docs to standalone entrypoint (#3855) 2026-06-04 17:23:04 +08:00
version.js fix(ci): resolve TS5055 release build failure since May 19 (#4383) 2026-05-21 16:01:35 +08:00
workspaces.js feat(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) 2026-06-11 21:57:20 +08:00