qwen-code/.github
Shaojin Wen 313f191150
fix(autofix): make the brake's BLOCKED handoff a first-class round outcome (#9297)
* fix(autofix): make the brake's BLOCKED handoff a first-class round outcome

When the growth brake fires, feedback.md tells the address agent to stop BLOCKED with a handoff — but the output contract only accepted address-summary.md or no-action.md, so a round that followed the instruction died as 'finished without required output file(s)', the brake's decision text was buried under a generic failure.md, the report said 'could not produce a passing fix', and the job left a red review-address check that the next scan counts as new feedback. Observed on #9222 rounds 6/7.

The handoff becomes a first-class verdict end to end: run-agent.mjs honors an agent-written handoff.md (with no fix verdict) as a graceful exit the way it already honors failure.md, and shields it from the API-error retry reclassification; the verification gate reports outcome=handoff for a no-commit round with a handoff and no failure.md; finalize lets handoff pass without failing the job; the report step runs for this outcome, posts the handoff note with the eval marker (watermark advances — the feedback is consumed as evaluated), and names the stop honestly instead of reporting it as a failed fix. The skill now tells the agent exactly which file to write when the brake fires. A coexisting spec output still outranks the handoff, and failure.md coexistence keeps the failed classification, so crash paths are unchanged.

* fix(autofix): align the handoff outcome's consumers and pins with its contract (#9297)

Review found the new handoff outcome breaking two pinned helper tests
(stale breaker-headline wording, unclassified headline in the fleet-shepherd
contract test), misreporting handoff rounds in the status-comment finalize
step, and leaving the whole handoff chain unpinned against mutation.

- Update the breaker headline pin to the PR's reworded headline.
- Classify the handoff headline as transient in the shepherd contract test
  and drop its "AutoFix stopped" prefix so the shepherd's terminal-only
  REASON regex cannot capture a transient stop (the shepherd workflow itself
  stays outside this round's footprint).
- Include handoff in the Finalize-status published-report branch.
- Give deliberate stops their own takeover-digest census bucket instead of
  the residual crash/infra bucket (EN + ZH).
- Neutralize :: workflow commands at the two new handoff echo sites.
- Use the runner's non-empty missing() convention for handoff.md so an
  empty file cannot read as a verdict in one layer and not the other.
- Correct the run-agent.mjs precedence comment: when a handoff coexists
  with a spec output, the gate (handoff branch first) decides the round,
  matching the documented "handoff + no-action -> handoff" contract.
- Pin the handoff chain where its siblings are pinned: finalize replay,
  POST_HANDOFF replay, mark/headline replays, the gate's no-commit decision
  table, the stub-runner handoff/empty/API-error cases, the report-step
  if-clause, and the census needle-to-emit cross-pins.

* fix(autofix): classify a no-commit handoff before the gate's structural checks (#9297)

Review proved the new handoff classification unreachable exactly where
the brake fires: the structural pre-checks (core rebuild, settings
schema, contracts) judge the PR's own diff and reject before the
no-commit fork, and the growth brake fires on precisely the red PRs
whose diff trips them. A compliant handoff (no commit, only handoff.md)
then classified as a retryable failure, so the repair pass deleted
handoff.md and could commit against the brake's explicit stop — the
self-feeding loop the handoff exists to prevent. Reproduced with the
real gate script: schema-check-fail + no-commit handoff exited 1 with
no outcome=handoff.

Move the no-commit handoff classification above the structural checks
(right after the failure.md exits, which keep their precedence). A
handoff claims nothing — acted=false, deferred to a human — so the
checks' false-no-action rationale does not apply, and the retryable/
repair machinery must never engage on a round the brake told to stop.
The no-op fork reverts to no-action-only classification.

- Add a gate test: stale schema + no commit + handoff.md classifies
  outcome=handoff, exit 0, no retryable (fails on the pre-fix gate).
- Pin the handoff-note :: workflow-command neutralization in both
  layers (the gate's sed and the runner's replaceAll), which review
  showed were surviving mutations.

* fix(autofix): reject a no-commit handoff written over a dirty workspace (#9297)

* fix(autofix): report a dirty-handoff rejection honestly, not as a failed fix (#9297)

* fix(autofix): reject a handoff written beside a round commit, non-retryably (#9297)

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

* fix(ci): classify the committed handoff shape as its own non-retryable outcome

R7-1 on this PR: a round that HAS a commit beside handoff.md skipped both brake-violation guards (clean tree misses the dirty guard; committed ref misses the no-commit branch) and fell through to the structural checks, where reject_fix defaults to retryable and the repair pass deletes handoff.md and may commit again against the brake's stop.

Classify it before the structural checks under its own outcome committed_handoff, sibling of dirty_handoff: non-retryable, its own honest report headline (reusing dirty_handoff's wording would claim nothing was committed when a commit exists), listed among the report-publishing outcomes in the status classifier, and never routed through finalize's pass list. Pins updated in the same pass: the committedWithHandoff gate case now expects committed_handoff with no retryable, the shepherd contract test classifies the new headline as transient (loop stays engaged), the status-classifier pin names all five outcomes, and the handoff-contract gate test gets an explicit subprocess budget (eight fixture arms outgrew the 5s default).

* fix(ci): count committed-handoff rounds in the milestone census rejected bucket (#9297)

* fix(autofix): publish brake violations green and preserve handoffs across crashes (#9297)

Two Critical review findings on the handoff output contract.

Brake-violation rounds (dirty_handoff / committed_handoff) ended with a
red review-address check: the eval marker stamps ts=NEWEST, strictly
before the check completes, and the scan counts failed checks completed
after the watermark — including this workflow's own review-address
checks — as new feedback. The next scan re-selected the PR and burned a
full agent round on the item the posted headline promised not to retry,
once per violation. Admit both outcomes to the green finalize arm the
way the clean handoff already is (the diff's own comment names this
self-feeding loop as the reason handoff went green), and key the report
step's routing and POST_HANDOFF trigger on the outcomes themselves so
the green rounds still publish their honest headline, handoff note, and
eval marker instead of going silent.

A crash, budget kill, or loop guard after the agent wrote handoff.md
synthesized a failure.md that shadowed the note: the gate reads
failure.md first (outcome=failed), the report preferred it, and the
timeout sentinel re-handed the item the brake stopped. Preserve the
agent-written handoff in the crash branch (exit 0, mirroring the
agent-written-failure.md arm), and never let writeHandoff overwrite a
non-empty agent verdict.

Both findings reproduced against this commit's verbatim code before
fixing: the case/jq replay showed the violation check red and counted
as new feedback, and a stub run showed the synthesized failure.md
shadowing the handoff. New behavioral tests fail pre-fix and pass
post-fix.

* test(ci): give four subprocess-heavy replays explicit budgets

The milestone digest, stale-duplicate revalidation, deny-by-default footprint, and recoverable-API-render tests spawn multiple bash replays of the real workflow/gate scripts each; the files those replays parse grew with this PR's handoff chain, and all four outgrew the 5s default (each verified to pass with an explicit 30s budget, matching the suite's convention for subprocess-heavy tests).

* fix(ci): mirror the handoff outcome consumers into the recovery clone (#9297)

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-20 08:32:43 +00:00
..
actions ci: run Windows merge queue tests on ECS (#8386) 2026-08-05 12:14:42 +00:00
assets fix(cli): echo resume command to main screen on exit (#8455) 2026-08-08 04:29:31 +00:00
ISSUE_TEMPLATE
scripts fix(autofix): make the brake's BLOCKED handoff a first-class round outcome (#9297) 2026-08-20 08:32:43 +00:00
workflows fix(autofix): make the brake's BLOCKED handoff a first-class round outcome (#9297) 2026-08-20 08:32:43 +00:00
actionlint.yaml ci: add Windows runner smoke test (#8008) 2026-07-29 08:44:36 +00:00
CODEOWNERS chore(ci): Add security hygiene: CODEOWNERS for release workflows, least-privilege permissions, security checks and Scorecard (#9008) 2026-08-14 01:22:53 +00:00
dependabot.yml
issue-owners.json feat(ci): auto-assign issues to area owners from labels (#8668) 2026-08-08 23:01:03 +00:00
pull_request_template.md docs(agents,pr-template): add Working Principles and restructure PR template (#4496) 2026-05-25 19:15:35 +08:00
release.yml ci: auto-skip internal CI changes in release notes (#7251) 2026-07-20 11:10:55 +00:00
spam-blocklist.txt ci: auto-minimize comments from org-blocked users (#7899) 2026-07-29 23:37:42 +00:00