mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-25 08:33:55 +00:00
632 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7e56bd0657
|
docs(autofix): add an operator guide for /takeover from N (#9622)
* docs(autofix): add an operator guide for `/takeover from N` `qwen-autofix.md` records why each line of the workflow is the way it is, indexed by code site — af-007 for the `from N` parser, af-016 for the marker it writes. That answers "why is this code shaped like this", but a maintainer deciding whether to seed a takeover has a different question: what do I type, what number do I pick, and what happens next. Nothing answered that. This adds a task-oriented sibling guide covering the problem the seed solves, how to choose N (with a table of remaining suggestion budget per seed), the three semantics that surprise people — the seed is a floor for an empty window rather than an offset added to every round, it dies with its counting window so `/retry` and a bare `/takeover` both return the counter to zero, and it is clamped strictly below the round cap while the audit record still cites the number that was typed — and the two things it deliberately does not do: seed the growth brake, or change what Critical-only keeps flowing. Every row of the accepted/rejected command table was produced by replaying the merged parser fragment verbatim, so the doc records observed behaviour rather than intent: `from 04` and `from 08` seed 4 and 8 rather than tripping octal, `from 0` engages as the explicit no-seed spelling, and `stop from 4`, a doubled space, a 3-digit number, and a prefixed or suffixed body all fail closed to no label and no seed. Cross-linked both ways: the design record's preamble now points at task-oriented guides, and the parser keeps its af-007 pointer with an operator-guide line beside it. The workflow grows by 64 bytes, which the size gate covers. * docs(autofix): align the round-seed guide with the workflow behavior Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
e40263ee55
|
chore(deps): Clear high-severity CVE baseline and harden the security gate (#9584)
* chore(deps): Clear high-severity CVE baseline and harden the security gate - Bump OpenTelemetry stack to 0.221.x (fixes @opentelemetry/core advisories) - Bump @larksuiteoapi/node-sdk to ^1.73.0 and override axios to ^1.19.0 - Bump mobilewright to ^0.0.53 (drops vulnerable sharp 0.34.x) - Bump markdown-it to ^15.0.0 (drops vulnerable linkify-it 5.x) - Update undici/fast-uri/brace-expansion/ip-address within range - Adapt telemetry code to OTel API changes (forceFlush, processor options) - Make security-checks a hard gate now that the high baseline is clean * chore(deps): Refresh mobile-mcp vendored lockfile to drop vulnerable sharp * fix(telemetry): stub sdk-node 0.221 env auto-config helper packages sdk-node 0.221 extracted its env-based auto-configuration into @opentelemetry/configuration, otlp-exporter-base, and otlp-grpc-exporter-base, which it now requires eagerly. The existing esbuild stub only covered the exporter-* packages, so the OTLP protocol chain (grpc-js, protobufjs, otlp-transformer) re-entered the sdk-impl static closure and tripped the serve fast-path bundle guard. Stub the three helper packages when imported by sdk-node only; our own protocol modules keep resolving the real packages. qwen-code never reaches these helpers at runtime (explicit exporters + env scrub). * fix(telemetry): disable metrics fallback without reader * fix(vscode): restore nested dependency notices * fix(deps): declare bundled punycode so its notice survives regeneration The CLI esbuild config aliases punycode to the userland package (esbuild.config.js), so the shipped CLI bundle contains MIT-licensed punycode@2.3.1. Its NOTICES.txt section was lost because the only lockfile paths reaching punycode were dev-only; the notice walker (rooted at vscode-ide-companion) never sees a production declaration. Declare punycode as a direct production dependency of the CLI (the bundle input) and of vscode-ide-companion (which packages the bundled CLI into the VSIX and owns NOTICES.txt), then regenerate the lockfile and notices so the MIT notice is restored. |
||
|
|
575e62ee46
|
fix(autofix): bind the sandbox image to its pulled digest (#9527)
* fix(autofix): bind the sandbox image to its pulled digest The sandbox image was exported as a mutable tag. `docker run <tag>` resolves against the local store without re-pulling, so a co-resident process with daemon access can `docker tag` different content under the same name between the resolve step and the consumer. Export the `<repo>@sha256:...` RepoDigests entry that matches both the pulled repository and the digest the pull itself reported: RepoDigests is shared by every tag of the same content, so index 0 can move off the pulled repo under a same-content retag, and retagged foreign content keeps its own repo — only the pair binds the export to what the pull fetched. Pin the daemon endpoint for both spawns. The docker CLI resolves its endpoint from DOCKER_HOST, then --context, then DOCKER_CONTEXT, then `currentContext` in the pool-shared config.json; clearing DOCKER_CONTEXT falls through to that last one, so the context is named explicitly and DOCKER_HOST is dropped from the child environment. An inspect answered by someone else's daemon hands back any digest it likes. Write the step files through a non-blocking, type-checked append. $GITHUB_ENV and $GITHUB_OUTPUT live under the runner-writable temp tree, where a planted FIFO turns a plain append into a block until the step timeout. Extracted from #9214, which is frozen; these were R11-1 and R11-2 there. The inspect timeout is now injectable so the tests can pin it, and the suite covers the endpoint pin on both spawns, the FIFO and directory refusals, cross-chunk stdout accumulation, and the timeout itself. Each new test was checked against a mutant of the code it pins. Refs #9089, #9524. * fix(autofix): bind gate image inputs to the resolver step output (#9527) * fix(autofix): revert repo-hygiene binding outside PR footprint (#9527) The deterministic gate rejected the previous commit because repo-hygiene.yml is CI machinery this PR never touched; review feedback alone cannot authorize changes there. Restore the file byte-for-byte and scope the workflow contract test to the two autofix workflows this PR binds. The repo-hygiene binding is real and is deferred to the review-findings follow-up queue for a maintainer-owned change. * fix(autofix): harden sandbox image consumers per review round (#9527) - R1-2: extract the duplicated spawn guard (endpoint pin, settle-once finish, SIGKILL timer, stdout capture, error/close wiring) into one spawnDockerCapture helper; pullImage and repoDigestOf share it. - R2-1: contract test fails when a workflow detects zero sandbox consumers instead of passing vacuously. - R2-2: success-path e2e test for the digest-bound export; verified it kills the exportImage(image) mutant. - R2-3: pin the daemon endpoint (DOCKER_HOST: '', DOCKER_CONTEXT: default) on every sandbox-consuming step, closing the $GITHUB_ENV and pool-shared currentContext channels past the resolver; contract test enforces the pin. - R2-4: gate the repair step on the resolver outcome so a failed resolver can never relaunch the agent unsandboxed. Also updates the workflow source pin in scripts/tests to the shared helper's literals (required by the R1-2 refactor). * test(autofix): pin repair outcome gate, derive contract set (#9527) - R3-1: the contract test now requires every always()-gated consumer to also gate on the resolver step outcome, pinning the R2-4 fail-closed clause; verified that deleting the guard from the repair step now fails the suite (the mutant shipped green before). - R3-2: route both main() e2e tests through withDockerStub; the refusal test's untouched-file asserts move before the temp-dir cleanup — they previously ran after rmSync, so they passed no matter what the resolver wrote. - R3-3: derive the contract test's protected workflow set from the tree instead of a hand-enumerated list, so a new resolver step cannot land untested; repo-hygiene.yml stays in an explicit, staleness-checked exception set until its deferred binding lands. * fix(autofix): pin resolver binary, make image check digest-aware (#9527) * test(autofix): share resolver e2e scaffold, tripwire stale exemptions (#9527) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
fee826c889
|
docs(ci): the ECS pool does run containers — correct two comments that say it does not (#9575)
* docs(ci): the ECS pool does run containers — correct two comments that say it does not Both files record, as the reason for a decision, that the self-hosted `ecs-qwen` pool has no container runtime. It does. `qwen-autofix.yml`'s `review-address` runs with `sandbox: "docker"` on those labels, behind a `docker info` preflight that fails the job outright if the daemon is unreachable, and it passes there — measured on `ecs-qwen-runner-hk-*` and `ecs-qwen-runner-sg-*`, with autofix's own comment noting that qwen-triage's container jobs prove the same pool independently. What is true, and is what the exit-44 note was really about, is the spelling: `sandbox: true` makes the CLI probe for a runtime and exit 44 when none answers, while `sandbox: "docker"` names one. No behaviour changes. `resolve-pr` stays on `ubuntu-latest` — the ephemerality is a good reason on its own for a job that merges the base branch and pushes to the PR head, and the comment now gives that reason instead of the false one. Triage's missing `sandbox` key stays missing; it is now recorded as a choice rather than as a capability it lacks. This matters because both comments are the first thing anyone picking up #9556 (whether the review pipeline should keep granting code execution as the invoking user) will read, and they price the sandbox option as a runner-capacity decision when it is a settings change plus a mount audit. * docs(ci): attribute the sandbox difference to daemon state, not spelling (#9575) * revert(ci): restore the triage contract test — CI machinery outside this PR's footprint (#9575) Deterministic verification rejected the previous commit because it reworded rationale comments, test names, and assertion messages in .github/scripts/qwen-triage-workflow.test.mjs — CI/verification machinery in an area this PR never touched. Review feedback alone cannot authorize that change, so the file is restored byte-identical to the base. The reverted finding (the test still records "the ECS pool ships no container runtime" as rationale) was verified real and is deferred to the follow-up queue for a maintainer-scheduled change. The two workflow-comment corrections from the previous commit stand: they are inside this PR's own footprint, and the restored contract test passes against them 118/118. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
2c64ebe980
|
feat(autofix): audit the approach instead of stopping on growth-budget breach (#9262)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(autofix): audit the approach instead of stopping on growth-budget breach A growth-budget breach no longer escalates to a maintainer handoff that stops the takeover. The breach now makes the round a growth-audit round: the agent audits the PR's approach on two axes — KISS (name a simpler alternative or prove each piece load-bearing) and minimal change (every hunk traces to the problem, an accepted finding, or a failing check) — and records a machine-readable verdict that the verification gate requires. sound re-arms the counting window at the current size and the loop keeps solving; drift simplifies first, then continues; conflict is the only growth path to a human, parked idempotently until a trusted human responds. The old divergence ladder (over budget for N rounds and not shrinking → stop) terminated takeovers whose remaining work could still fit: the growth it punished was protocol-mandated pinned tests (#9213 stalled at round 5 with two small Criticals left). A size signal now triggers a judgment, never a stop. Design: docs/design/autofix-growth-audit.md * fix(autofix): update the artifact-list pin for the growth-audit.json upload entry * fix(autofix): surface conflict verdicts past the failure.md exits and strip verdict forgery channels (#9262) * fix(autofix): harden the growth-audit verdict pipeline and park wake set (#9262) * fix(autofix): close the verdict-pipeline forgeries and loop-generated wake entrances (#9262) * fix(ci): drop the retired divergence rationale records (af-046/af-047) from qwen-autofix.md --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
a074d3b042
|
chore(ci): Disable install scripts in release CI and guard security-checks workflow (#9577)
* chore(ci): Disable install scripts in release CI and guard security-checks workflow * fix(ci): complete release install hardening * test(ci): pin release install step count Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): scope release PAT to push step Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): export GH_TOKEN so the release-branch push uses CI_BOT_PAT * fix(ci): export GH_TOKEN so the credential helper sees it at push time An inline GH_TOKEN prefix only covers the gh auth setup-git call itself; the helper re-resolves the token when git push invokes it, so the push would fall back to the job token with persist-credentials disabled. * fix(test): anchor setup-git ordering check after the export line A comment in the push step mentions gh auth setup-git before the export, so indexOf found the comment first and the ordering assertion inverted. * style(test): wrap long line to satisfy prettier * fix(ci): address review findings on PAT handling and install comments - Pin gh auth setup-git before the git push it authenticates in both release and finalize workflow tests, so moving credential setup after the push no longer passes. - Correct the replay comment: npm run generate is not a lifecycle script and workspace lifecycle scripts stay disabled. - Drop the overstated push-boundary claim and record why the push needs the bot PAT rather than the job token. * test(ci): pin CI_BOT_PAT out of install steps and the publish job header * style(test): apply prettier's exact re-wrap for the two flagged calls * test(ci): pin CI_BOT_PAT out of the workflow-level headers too --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
9f2342d323
|
fix(ci): stop the fallback comment from denying a review it already posted (#9462)
* fix(ci): stop the fallback comment from denying a review it already posted The review job can fail AFTER posting its review — the CLI exiting silently, a cleanup step dying — and both fallback sites then announce that review as one that could not be posted, retry instruction attached. Measured on PR #9342: the review posted at 11:56:34Z, review-pr failed at 12:00:53Z ("Qwen review completed but produced no output"), and the comment landed at 12:01:00Z saying the pipeline "failed before a review could be posted. … retry with @qwen-code /review" — a fresh ~3-hour review, asked for beside the review that had just landed. The autofix takeover loop reads the same feed a human does. Both sites now check, before composing a body, whether a review this run posted is already on the PR. The check is scoped three ways so a stale review can never buy silence on a genuinely dead pipeline: the bot's own account, the head this run reviewed, and a submission at or after this run started. Where the proof is unavailable — no start time, no head, a failed listing — the guard declines to fire and the comment posts, the same call the head-moved guard already makes. The job-level step now reads state and headRefOid in one `gh pr view` (the in-job step already did), which is where its head value comes from. Tests run the steps' real bash over review fixtures, because the guard IS a filter: silence when this run posted the review, and posting for each near-miss on its own — an earlier run's review at the same head, another account's, one of a different head, a PENDING one, none at all, an unavailable start time, and a failed reviews listing. One existing assertion tightened: "no `gh run view`" was the proxy for "no head comparison on comment runs", and the new guard asks that same command for startedAt on every event, so it now pins the head lookups themselves. The stub's state,headRefOid branch learned the pr_closed scenario its state-only sibling already knew. * fix(ci): anchor the already-posted guard on the run's creation, and say when it cannot run Round 1's two blockers, both re-verified against this repo's own run data. The time anchor reset on job re-runs. `gh run view --json startedAt` returns the LATEST attempt's start while the run id stays the same — the dedup above relies on that stability — so a re-run pushed attempt 1's review outside "this run": runs 32219268680 (created 05:23:57Z, startedAt 05:51:26Z) and 32218596441 (05:13:04Z → 05:22:05Z) both show the ~9-28 minute shift. Attempt 1 posts its review, the job fails after the post, someone re-runs it, attempt 2 fails before posting — and the guard, anchored on attempt 2's start, lets the contradictory comment through. Exactly the shape this PR exists to stop, on the path most likely to reach it. Both sites now anchor on `createdAt`, which is attempt-stable; a review submitted after the run was created still cannot belong to an earlier run, so the stale-review protection is unchanged. The guard also swallowed its own lookup failures. A transient failure in either call emptied the value, the guard declined, and the false comment posted with nothing in the log separating "the guard ran, nothing matched" from "the lookup died" — while every sibling lookup in these steps announces its failures. Both unavailable paths now emit a `:⚠️:` and a step-summary line before posting. No behavior change: posting was, and remains, the fail-open direction. Tests: a re-run fixture per site, where the stub answers `createdAt` and `startedAt` with DIFFERENT values and attempt 1's review sits between them — reverting either site to `startedAt` fails exactly these two; and a per-site assertion that both unavailable paths announce themselves. Also from round 1, both verified before taking: the stub's standalone `*state*)` branch is dead (no `--json state` call remains in either extracted step) and is removed, so its scenarios cannot be edited into a no-op; and the harness now substitutes `${{ vars.* }}` before running the in-job script, which bash rejected as a bad substitution — the assignment was skipped, `MAX_TIMEOUT_MINUTES` stayed unset, and eight error lines rode every suite run, so "the step's real bash" was not quite true for that line. * fix(ci): read the head this run reviewed, and claim only what the guard proved Round 2's six, all taken. The fallback JOB compared review commit ids against the PR's head at fallback time, not the head the run reviewed. On every trigger but pull_request_target the head-moved guard above deliberately does not run, so a push landing between the post and this step leaves that value pointing at bytes no review ever covered: the match fails and the contradictory comment posts anyway — the #9342 shape, re-opened for the trigger + post + push + fail-after-post interleaving. `review-pr` now publishes the head its review step recorded as a job output, and the guard reads it, falling back to the fresh head only when the job died before that step (a run that posted nothing either). The in-job twin needs none of this — its unconditional head-moved check exits first — and that asymmetry is now pinned per site rather than left to be rediscovered. Both skip messages claimed "this run already posted a review". Reviews carry no run id, so the window (bot account + head + submitted at or after this run was created) also matches an overlapping sibling run's review, which this workflow's own concurrency note says can happen. The suppression is right either way — a review IS sitting above the comment — but the oncall reading the summary was told something the guard never proved; both now say what it did. The guard's opening paragraphs still described the round-1 `startedAt` anchor while the code (and the paragraph below it, and the runtime warning) said creation. A maintainer reading top-down got the anchor that re-runs break — the defect round 1 removed. Test stub: `gh run view` now answers by running the caller's own --jq over an object carrying both timestamps, instead of a `case` on "$*" that matched substrings in order. A combined `--json createdAt,startedAt --jq '.startedAt'` was answered from the createdAt branch, leaving the re-run pin green for a guard reading the attempt-scoped field — the exact regression it exists to catch. * fix(ci): attribute the guard by time alone — the head is not a stable run attribute Round 3's blocker, and the second time the head clause re-opened the contradiction this PR exists to close. Two entrances this round, both after a "Re-run failed jobs": attempt 2 dies before the review step writes its head, so the guard falls back to a head attempt 1 never reviewed; or a push lands and attempt 2 records the NEW head — in both, attempt 1's own review no longer matches `.commit_id`, and the fallback posts "failed before a review could be posted … retry" beneath the review the same run had posted. Rather than patch the head lookup a third time, the head clause is gone. What the guard proves is now narrower and stable: a bot review of this PR was submitted while this run was alive — bot account plus the attempt-stable `createdAt` window. That closes both entrances at once and takes the round-2 cross-job wiring with it (review-pr's `expected_head_sha` output and the env line that read it), so there is no untested chain left whose silent breakage would restore the fresh-head comparison. The job-level step no longer needs the PR head either and reverts to its state-only query; the test stub's state-only branch, removed in round 1 as dead, has a caller again. The comment blocks now state the guarantee the concurrency model actually supports. They claimed a review inside the window "cannot belong to an earlier run", but per-run concurrency groups deliberately allow overlapping runs on the same head, so an earlier-created run's review can match and this run's failure then goes unannounced. That is accepted, and said plainly: the silence coincides with a bot review a reader can see — the very state that makes the comment's claim false — while the bot-author and creation-time clauses still rule out silence with no review at all. Tests: the moved-head case flips from "posts" to "silences" and is pinned per site (a review on ANY head inside the window silences); re-introducing a head clause fails exactly that test; and a structural pin asserts the wiring is absent rather than merely unused. * test(ci): skip the guard's jq-driven cases where jq is absent, instead of failing them The stub answers the guard's reviews and run-view lookups by running the caller's own `--jq` filter — that filter IS the thing under test — so those cases need jq on PATH. A reviewer running the suite on Windows without jq saw them as failures of the guard rather than as untested, which is the wrong signal in the wrong direction. Probed once per run and skipped honestly. Measured with a jq that exits 127: the file goes from 31 failures to 26 failures plus 13 skips — the 26 are the retry-loop cases, which have parsed the review log with jq since long before this change and are equally untestable without it. GitHub's windows-latest image ships jq, so CI coverage is unchanged either way; what changes is what a jq-less machine reports. * docs(ci): remove the head-keyed leftovers the guard no longer has Round 5's four, all leftovers of the round-3 design change rather than new behavior. The job-level block still explained why it compared against the head this run reviewed — naming `pr_head`, "the reviewed head's review" and a `review-pr` job output, none of which survive: the shipped filter is author scope plus the creation-time window, and the wiring was deleted with the head clause. A maintainer reading it would look for a comparison that is not there. The in-job block stated the createdAt-not-startedAt rationale twice, once with the measured run ids and once without; the measured one stays. Same in the tests: the stub's comment listed a head clause the filter deliberately does not have (`attributes by TIME, not by head` is the test that pins its absence), and the harness still declared and injected `reviewedHead`/`REVIEWED_HEAD_SHA`, which nothing reads since the wiring went — a knob that looks live and cannot be. * docs(ci): drop the duplicated anchor rationale and the last stale-head leftovers Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): exclude foreign same-account approvals from the already-posted guard * fix(ci): attribute the already-posted guard by composed-review markers The foreign-approval exclusion list shipped incomplete: the triage skill's commit-pinned APPROVE body also posts under the same account, matches the guard's author and window clauses, and silenced the fallback for a genuinely dead run — the failure shape this guard exists to stop. The producer set is open, so no exclusion list can be finished; every miss fails in the dangerous direction. Match positively instead: a review silences the fallback only if its body carries what only this pipeline's composed reviews carry — the "via Qwen Code /review" attribution footer or the invisible qwen-review-ledger marker. Every composed body carries at least one (a zero-findings APPROVE included); no foreign approval carries either. A marker that ever changes shape stops the guard firing and the comment posts — the pre-guard status quo, not a masked dead run. --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
d6732eaa5d
|
fix(ci): author the release PR with a third bot PAT (#9592)
GITHUB_TOKEN cannot create the release PR because the org disables GitHub Actions from creating or approving pull requests. The repo-level switch is rejected with 409 and the org-level switch requires org admin. Author the PR with CI_REVIEW_BOT_PAT (a third identity) instead, so ci-bot and dev-bot can both approve without self-approval blocking. |
||
|
|
c59910ba3f
|
fix(ci): make autofix finding replies idempotent (#9463)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* fix(ci): make autofix finding replies idempotent A crash-and-rerun of an address round, a same-run repair that regenerates the dispositions, or a later round re-declining the same finding all reproduce the same comment-replies.json entry — and the reply step posted it again, landing identical bot replies on one thread (observed 2026-08-16: one identical reply posted three times, #9296). The thread fetch now also reads each comment's author and body, and the reply step skips posting when the thread already carries a comment by the autofix bot whose body equals the neutralised body about to be posted. A changed body — new information from a later round — still posts; a threads view without author/body, or a stale/empty one, degrades to the old post-always behavior. The replies API itself is already the no-review-event path, so this PR only adds the missing idempotence (the P1 replies item of #9296). Refs #9296 * fix(ci): restore inner pageInfo in autofix threads query and pin reply-gate contracts (#9463) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
027772d2ab
|
test(ci): stage on-disk session state in the serve A/B (#9444)
* test(ci): stage on-disk session state in the serve A/B The serve A/B drives every scenario against a freshly started, empty daemon, so the entire session-admission surface — case resolution, transcript integrity, active/archive conflicts, reserved sources — is unreachable and a PR that rewrites it diffs as "no response changes". #9341 is the worked example: the posted A/B reported no change across 4 scenarios while the same build pair, driven with transcripts on disk, answers differently on six requests. Scenarios can now stage transcripts before their request and capture a reduced projection of the response, and the HTTP status is recorded on every capture so a status-only difference is visible. Six session-admission scenarios use that: a healthy restore, the legacy uppercase spelling, case-only twins, an unreadable transcript, one id in both the active and the archive directory, and creation carrying a source type. The staged fixtures depend on the on-disk project layout, which the harness mirrors rather than imports. If that mirror ever drifts the transcripts land nowhere and every staged scenario would quietly answer 404 on both arms, so the healthy restore doubles as a canary that fails the drive instead of publishing a reassuring all-clear. * test(ci): address the R1 review round on the serve A/B harness Clears the capture directory before a drive writes into it, so a re-run can never let an earlier run's files stand in for scenarios this run did not capture, and writes a completion marker once every scenario is captured. A baseline without that marker is now reported as partial, because a base drive that stopped part-way leaves the scenarios it never reached rendering as "this PR adds these responses" — the same shape a genuinely new scenario produces. Both arms are driven by the head checkout's harness, so a capture pair always carries the status field on both sides and the compatibility shim for a base that predates it was unreachable; it and its tests are removed rather than left to teach a transition the wiring cannot produce. Non-object response bodies are now nested instead of spread, which dropped scalars and re-keyed arrays. The source-type scenario probed a type today's daemon does not reserve, so it never reached the refusal branch it was named for. It is split: one scenario pins the source the daemon actually reserves, the other keeps an ordinary type that a future reservation would move from admitted to refused. A second canary covers the archive directory, which nothing certified before — a drifted archive name would have left the conflict scenario loading from the active copy on both arms and diffing clean. The remaining inert request body key is gone too; the client id is read from a header, never the body. The harness tests were passing under mutations they appeared to cover: the staging routing, the projection guards and the fixed-id requirement are now pinned by assertions that fail when those are inverted. * test(ci): close the R2 gaps in the serve A/B harness The completion marker was declared twice, once by the writer and once by the reader, with nothing pinning the copies together: renaming one side left both suites green while CI would either flag every complete baseline as truncated or stop noticing truncated ones. The drive now owns the constant and the diff imports it. Two invariants the code asserted in comments were not enforced. A response body carrying its own status key overwrote the status the harness saw, so a status-only regression on such a route would have diffed as an unchanged body; the harness value now wins. And the canary check — the harness's only drift alarm — had no test at all: inverting it so it could never fire left every test passing. It is now a named helper with tests on both branches. The archive canary pinned an exact status, which conflates its precondition with the product's decision: if an archived-only load ever becomes loadable, the precondition still held, but the drive would abort and suppress the very row the captures already contained. It now fails only on the one answer that means the staged file was never seen. Finally, nothing pinned that a staged scenario probes an id it actually staged. Staging the wrong id answers 404 on both arms, captures identically, and drops that branch out of coverage with every test green. * test(ci): close the R3 gaps in the serve A/B harness The completion marker proved that some drive finished, never that this run's did. The only reset lived inside the drive script, which does not run when an arm is skipped before it starts — no merge-base resolved, the base checkout failing, or its build dying — and on the persistent pool the capture paths outlive a run. An inherited baseline then arrived complete, marker included, so neither degraded-baseline warning fired and the comment would have diffed this head against another run's base. The workflow now clears both capture paths in an unconditional step, which is the only place that covers a skipped arm. The in-script reset also turned a write-only script into an unguarded recursive delete of a path taken straight off the command line, which the documented local usage invites a reader to mistype. It now refuses any directory that holds something other than captures. The healthy canary's premise was wrong: the product validates transcripts record by record and fails open, so a fixture whose records stop validating restores as an empty session and still answers 200. Measured against a real daemon, a wholly drifted fixture passed the canary and left every staged scenario probing an empty daemon — the false all-clear this harness exists to prevent. The canary now keeps a replay-size witness in its capture and fails when it is zero. Three test gaps behind the same theme: the marker's writer, the comment subcommand that CI actually invokes, and the mixed-case scenario's existence were all unpinned, and the staged-id check asserted against the union of every scenario's staging rather than the one under test. The capture loop is extracted so its ordering is testable without a daemon. * test(ci): cover the setup-failure abort in the serve A/B capture loop The capture loop was extracted so its ordering could be pinned without a daemon, and three of its four abort branches were covered — but not the one that fires when a scenario's setup request fails. Dropping that throw left the whole suite green while a capture would be recorded against a daemon where the setup never took effect, which is the masked diff the branch exists to prevent. * test(ci): close the R5 gaps in the serve A/B harness * fix(ci): send an admitted source in the serve A/B unreserved-source witness --------- Co-authored-by: wenshao <nigolaschao777@gmail.com> |
||
|
|
099a71c936
|
fix(ci): heal a symlinked workspace instead of wedging the runner on it (#9498)
* fix(ci): heal a symlinked workspace instead of wedging the runner on it The hardened wipe guard refuses any workspace that canonicalizes outside the runner workspace. That refusal is correct, and it created a permanent failure: when a previous job leaves the workspace replaced by a symlink pointing outside — or by any non-directory — the guard resolves it to the target, refuses, and exits 1 having removed nothing. Nothing else clears that state, so every later job on the runner dies at the same line, forever. The pre-guard code wiped through the link and self-healed by accident. Reproduced against main's own step text before this change. Heal it: the link itself lives inside the runner workspace and is safe to unlink, and only once it is gone can a legitimate wipe proceed. The layer has to sit before canonicalization — afterwards the path has already resolved to the target and the allowlist refuses before any repair can happen — which means it judges a raw path, and that is where the first attempt at this (closed with #9369) went wrong. A raw `"$RWS"/*` match accepts `$RWS/link/sub` as a string while the kernel resolves it through an intermediate symlink to a file outside the runner workspace, so the unlink and the mkdir landed outside and only then did the allowlist refuse the wipe. Here the containment is judged on the canonicalized PARENT — never on $WS, which would resolve through the very link being removed — and the unlink then acts on the raw path, so it takes the link and never follows it. Four more constraints the same review surfaced: the raw trailing-slash strip moves ahead of the predicates (both `[ -L "$WS/" ]` and `[ ! -d "$WS/" ]` resolve through a link and report its target, so one slash hides the corruption); the allowlist root is prepared before the heal, since it bounds it, and an empty $RUNNER_WORKSPACE would degenerate the containment pattern to the match-all `/*`; both the unlink and the mkdir fail closed, because under `-e` a failure that is not the last command of an && list is swallowed and would leave the wipe running on a corrupt path; and the heal logs what it found and where the link pointed, since this incident otherwise leaves no trace at all. All three copies get it — the two triage wipes and the A/B wipe — with per-suite fixtures: the wedge healed (link gone, directory recreated, target's contents intact), the intermediate-symlink attack refused with the outside file unmutated and zero rm calls, the non-directory half, the trailing-slash spelling, the fail-closed unlink, and the ordinary workspace where the heal must not fire at all. Mutation-checked layer by layer; each has a fixture that fails when it is removed. One pre-existing test changes meaning: the canonicalization pin used a symlinked workspace and asserted refusal, which is now the healed path. It moves to a vector the heal does not touch — an intermediate symlink whose far end is a directory — and keeps its mutation strength: with the canonicalization deleted, find resolves the link and hands the outside directory's entries to the rm recorder. Closes #9480 * fix(ci): keep the heal's log out of the workflow-command channel Three findings from the first review round on this layer. The heal logged the symlink's target inside a `:⚠️:` line. The target is bytes a PREVIOUS job chose — on the verify lane that job may have run a contributor's code — and the runner parses `::` at the start of any stdout line as a workflow command, so a target of $'…\n::error::forged' let the step reporting the corruption forge an annotation. The annotation now carries no untrusted bytes: the target is stripped of line breaks, capped, and printed on its own prefixed line, where a leading `::` cannot begin a command. Verified against the real step text — the forged line lands as data, and no output line starts with `::error::`. The mkdir leg's refusal had no executed fixture while its `rm -f` sibling had one. It does not need a permission trick: `rm -f` returns 0 for a path whose parent is not a directory (it reads as "already absent"), and the mkdir that follows cannot succeed — so the branch is reachable, and a swallowed failure there would run the wipe against a path that does not exist. Fixtures in both suites, and it runs as root too. And the post-run triage copy's header still said this copy "predates the checkout-heal hardening and never received it" while carrying the whole guard plus the heal directly underneath. That header is the in-code inventory the eventual convergence of these copies will read; understating it is how a sync strips layers in the wrong direction. * test(ci): drive both wipe copies in the remaining single-step heal fixtures * fix(ci): keep the Serve A/B job from timing out on slow runners --------- Co-authored-by: Qwen Autofix <autofix@qwen-code.dev> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
6fe9ce4886
|
fix(ci): stop counting wedged queued runs as in-flight in the shepherd (#9518)
* fix(ci): stop counting wedged queued runs as in-flight in the shepherd When GitHub refuses to start a workflow run it still CREATES it: the run sits `queued` forever with zero jobs and cannot be cancelled or deleted through the API. On 2026-08-19 an oversized qwen-autofix.yml produced a run like that from the shepherd's own liveness dispatch, and the watchdog counted it as in-flight for the next 18 hours: last scan signal: 2026-08-19T05:01:14Z (1107m ago), in-flight: 1 The age gate said "dispatch a scan", the in-flight gate said "one is already running", and nothing ever completed the run that would clear it. The loop stayed dark until a human looked. Treat a run still `queued` past ZOMBIE_QUEUED_MINUTES (30, overridable via the QWEN_SHEPHERD_ZOMBIE_QUEUED_MINUTES repository variable) as wedged rather than live. One `wedged` predicate is defined once and reused by the in-flight count, the conflict lever's busy-set, and a new census, so the three readers cannot disagree. Only `queued` runs wedge — a review-address run legitimately runs for hours — and a missing createdAt reads as brand new, so unknown age never licenses a duplicate dispatch. The wedge is now visible instead of silent: a :⚠️: names the count and the oldest one, the tick heartbeat carries `wedged-queued:`, and the dashboard carries a banner. Invisibility is what made this expensive — PR-event runs kept reporting success while every scheduled scan was dead. Verified against the real run list from the incident: the old predicate returns in-flight=1 (starved), the new one returns 0 with a census of 2. Behavioral tests replay both jq programs and the busy-set walk verbatim from the workflow. * fix(ci): keep shepherd busy-set job-verified and bound wedge re-dispatch (#9518) * fix(ci): reject degenerate zombie threshold and name the paused liveness gate (#9518) * fix(ci): name the recorded liveness run in the shepherd wedge remedy (#9518) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
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> |
||
|
|
0baaec2b32
|
chore(ci): Drop NPM_TOKEN in favor of npm Trusted Publishing (#9552)
* chore(ci): Drop NPM_TOKEN in favor of npm Trusted Publishing * chore(ci): Pin npm 11 for Trusted Publishing in release jobs * test(ci): Cover Trusted Publishing requirements |
||
|
|
b219e3a716
|
chore(ci): Add --provenance to npm publish and id-token permission (#9532)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* chore(ci): Add --provenance to npm publish and id-token permission * test(scripts): expect --provenance in npm publish step assertion PR #9532 adds --provenance to every npm publish in the release pipeline. Update the workflow-pinning test to match the new command so the helper test suite stays green. |
||
|
|
3b3818db87
|
fix(ci): keep qwen-autofix.yml under GitHub's 500 KB start-runs limit (#9517)
GitHub does not start runs for a workflow file larger than 500 KB (512,000 bytes) and reports nothing when it stops. qwen-autofix.yml crossed that line on 2026-08-19 at 512,782 bytes: schedule ticks stopped firing, every workflow_dispatch sat "queued" forever with zero jobs and could not be cancelled, and issues/issue_comment went quiet — while pull_request_review runs kept succeeding, because a PR event resolves the workflow from the PR's own branch and those carry older, smaller copies of this file. The loop therefore looked half-alive and stayed dark for a day. Move 75 long comment blocks (1,326 lines) verbatim into a sibling design record, .github/workflows/qwen-autofix.md, leaving each block's opening lines plus a `qwen-autofix.md#af-NNN` pointer where it sat: 518,055 -> 426,437 bytes. No executable line changes — the YAML parses to an identical document outside `run:`, every `run:` script still passes `bash -n`, and the only lines removed anywhere are comments. Steps that are duplicated verbatim across jobs share one pointer so they stay byte-identical. Add .github/scripts/check-workflow-size.sh (gate at 470,000 bytes), wired into CI on every profile: a .github-only PR classifies as `github_ci_only` and skips the `full`-only checks, which is exactly the PR that can trip this. Tests pin the gate, every workflow's size, and pointer/section symmetry. Delete qwen-autofix-recovery.yml. It was cloned during the incident on the theory that the workflow ENTITY was wedged, but it carried the same oversized file, so its dispatches queued identically and its schedule never fired. |
||
|
|
4154bd7457
|
fix(ci): no-op touch to re-register the autofix workflow triggers (#9479)
Since ~2026-08-19 00:00 UTC the workflow's schedule, issue_comment, and pull_request triggers stopped creating runs while pull_request_review kept working; dispatch runs were created but never expanded into jobs (three sat queued with zero jobs for 4-11 hours). The on: block was unchanged throughout and a disable/enable cycle did not restore dispatch, consistent with a stale trigger registration on the Actions backend. Any content change forces a re-parse; this commit is that change (one comment line). |
||
|
|
fd2dcd3fe0
|
fix(ci): clone qwen-autofix into a recovery workflow entity (#9482)
The Actions backend wedged the original qwen-autofix.yml workflow entity on 2026-08-19: runs stick "queued" with zero jobs and cannot be cancelled or deleted via API, schedule ticks stopped being created, and event- triggered runs are dropped. Same-repo control workflows run normally, so the failure is bound to that one workflow entity. A byte-identical copy under a new path registers as a fresh entity and resumes the loop; the original file stays untouched so the revert is deleting this one file. |
||
|
|
517a2bfc28
|
fix(triage): compute the flake-gate diff before the env -i re-exec (#9468)
* test(triage): pin the parent-computed diff and the child copy Update the record-step pins to the RUNNER_TEMP-staged file and add a pin asserting the scrubbed child copies it rather than re-running git. * fix(triage): compute the flake-gate diff before the env -i re-exec The scrubbed (env -i) child cannot read the shallow merge-ref objects: git global safe.directory lives under HOME, which env -i strips, so git refuses to read the base commit and the diff fails with "Could not access <base-oid>". Compute the NUL-delimited diff in the parent (normal environment, no PR code executes) and stage it under RUNNER_TEMP; the clean child copies it into the root-only gate home. * fix(triage): rm -f before the parent diff redirect, slash-path the parent commands * test(triage): pin diff-before-re-exec ordering and slash-pathed commands * fix(triage): harden the flake-record staging path against directory and symlink plants |
||
|
|
5003ab3c7f
|
feat(web-shell): add transcript contract prevalidation (#9388)
* test(web-shell): add transcript contract prevalidation Freeze reproducible evidence for current transcript paths before any VS Code or HTML export production migration. - Add versioned fixtures, closed export schema, and capability gates - Probe direct-daemon and ACP identity under partial history prepend - Preserve raw adapter semantics and full write_file Turn Output diffs - Document the two-MR architecture, security constraints, and blockers * fix(web-shell): harden transcript prevalidation gates Make the evidence-only contract suite enforce the review assumptions it documents while preserving the existing runtime transcript behavior. - Run the contract suite in the required no-AK integration job - Fail closed on ambiguous identity probes and deduplicate gate kinds - Enforce manifest, hash, export safety, and renderer version boundaries - Cover visible transcript text and stable Desktop packaging semantics - Record the complete PR comment evaluation and verification outcome * fix(web-shell): close transcript prevalidation gaps * fix(web-shell): remove brittle Desktop wiring probe Keep transcript contract prevalidation at the evidence level it can actually prove. The previous source-text assertion could both reject equivalent formatting and pass unreachable packaging code. - Remove the Desktop script parser and its false behavioral claim - Mark installed-artifact verification as deferred to Desktop smoke tests - Clarify MR1 matrix, CI wiring, and provenance evidence boundaries - Refresh the hash-locked capability matrix fixture Note: This does not change Web Shell or Desktop production behavior. --------- Co-authored-by: heyang.why <heyang.why@alibaba-inc.com> |
||
|
|
0530cb04df
|
fix(triage): diff the flake-gate file list against the pinned base OID (#9464)
The "Record changed test files for the flakiness gate" step runs its `git diff 'HEAD^1' HEAD` inside the env -i scrubbed child. On the persistent pool, resolving the `^1` parent there intermittently fails with "Could not access 'HEAD^1'" — the shallow merge-ref object store is left unreadable by prior `--depth=2` fetches — which takes down the whole verify lane. The "Pin agent inputs" step already captures the base OID to $RUNNER_TEMP/verify-base-oid while .git is still root-owned. Diff against that content-addressed OID instead of re-resolving the parent: it needs no parent walk and is the same value the workflow already trusts for its post-build re-pin. Add a pin asserting the record step reads the recorded OID rather than re-deriving HEAD^1. |
||
|
|
1eb8a0c7f8
|
feat(review): wire --resume through /review and the review run subcommand (#9153)
Surface the local resume feature (PR #9092) on the paths a user reaches it from: - `parse-args.ts`: `/review <pr> --resume` parses to `resume: { requested, effective }`, gated on PR targets (a local review's diff comes from a live working tree with no stable interrupted state). A `--resume` on a non-PR target warns and is inert. - `run.ts`: the `qwen review run` headless wrapper takes `--resume` and passes it through to the `/review` prompt. - `SKILL.md` Step 1 gains a "Resuming an interrupted run" branch: on `resume.effective`, append `--resume` to `fetch-pr`, branch on its `resumed` JSON, run `recover-findings`, re-enter the audit loop at `latestReverseAuditRound + 1`, and read the restart bound back from `restartsSpent`. - `DESIGN.md` / `docs`: document resume as a LOCAL convenience. The CI review workflow runs FRESH — it does not pass `--resume`. A CI attempt runs no-sandbox on the reviewed PR's own code and its worktree is deleted the moment it exits, so there is no interrupted state on disk for a retry to continue; a resume would refuse `worktree-gone` and start over anyway. The retry loop and its test assert the fresh-only wiring. |
||
|
|
b6e93d27ad
|
fix(autofix): paginate review threads instead of reaching the oldest 100 (#9390)
* fix(autofix): paginate review threads instead of reaching the oldest 100 `resolve_and_reply_threads` fetched `reviewThreads(first:100)` with no pagination. GitHub returns review threads in ASCENDING creation order, so a single page is the OLDEST hundred — on a long-running PR, precisely not the threads the current round is answering. Both blocks downstream map an inline-comment id to its thread. A thread past the page is absent from `THREADS_JSON`, so an implemented Critical is never resolved and reads as still open, and a declined finding's reply is answered by silence. Those are the two outcomes the function exists to prevent. Live: 8 of the 22 open takeover PRs exceed the cap. #8403 carries 1256 threads, so one page reached 8% of them — and all 1256 are unresolved. The code already detected this: it requested `pageInfo{hasNextPage}` and emitted a `:⚠️:` when true. It just never fetched the next page. Use `gh api graphql --paginate`, which is built for exactly this shape, and slurp its node stream into the flat array both blocks already expect. On #8403 that is 13 requests in ~10s. A partial fetch is USED rather than discarded: losing twelve good pages to a rate limit on the thirteenth would resolve nothing at all, so the failure is announced and the threads in hand still map. One residual stays open and is now announced rather than implied: a thread carrying more than 100 comments still truncates, so a comment past that page is unmapped and each block falls back to the id as given. No thread in the live pool comes close. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(autofix): keep the pagination failure's reason, and pin both warnings' absence Round 1's three Suggestions, all on the partial-fetch path this PR adds. R1-1: `2> /dev/null` on the paginated fetch discarded gh's stderr — the only text saying WHY pagination stopped. The warning announced THAT it stopped, so the oncall could not separate a transient rate limit (back off) from an expired PAT (rotate) or a network failure without re-running the ~13-request query by hand. Captured to `${WORKDIR}/threads-fetch.err` with the pattern already used elsewhere in this workflow, and its tail folded into the warning. R1-2: the outer thread pagination silently depends on the inner `comments` pageInfo NOT asking for `endCursor` — gh's paginator adopts the first pageInfo carrying both fields. The `Residual:` note actively invited a maintainer to close that residual by adding it, which would hijack the thread-page cursor and stop after page one at exit 0 with no warning, silently restoring the oldest-hundred bug. Documented as load-bearing, in the comment block above the fetch rather than inside the query literal — a `#` line there is transmitted. R1-3: both new warnings were asserted only in the positive, so a mutation making either unconditional shipped green. Added the clean-run absence assertions this file's own convention calls for (321 `not.toContain` uses), and the gh stub now writes a reason to stderr on failure so the folded-in text is assertable. Verified: qwen-autofix-workflow 178 passed. Mutation-checked — restoring `2> /dev/null` and making the pagination warning unconditional each fail a test. The one remaining failure (`behaviorally replays the stale-duplicate revalidation`, 5s timeout) is identical with these changes stashed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(autofix): keep gh's error body out of the slurped review threads Round 2 of the review on #9390 found the paginated review-thread fetch poisons its own output on a partial page, and asked for two clarifications around it. R2-C (Critical) — on a failing page gh skips `--jq` and appends that page's raw response body (a rate-limit message, or a GraphQL error envelope) to stdout after the good nodes. The unfiltered `jq -s '.'` slurped it as an extra element, and both consumers below iterate `.comments.nodes[]` over every element, so the first one exited 5. This step runs under errexit, so that aborted 'Push and report' AFTER a good push had landed — the report and the markers were skipped and the job failed. That contradicts the two invariants the block documents: a resolve failure must never fail a good push, and a partial fetch is used rather than discarded. The slurp now keeps only thread-shaped documents. R2-1 — the comment block warned against adding `endCursor` to the inner `comments` pageInfo, but the outer pageInfo's field ORDER is load-bearing for the same reason: gh's cursor scanner carries its flags across pageInfo objects and breaks at the first one yielding both fields, so alphabetizing to `pageInfo{endCursor hasNextPage}` stops after page one just as silently. Said so at the query, and at the test pin that goes red on a reorder, so the pin is understood rather than bumped. R2-2 — the stderr fold dropped the `tr '\r\n' ' '` that its ten sibling sites apply. Actions parses workflow commands line by line and gh's secondary-rate-limit stderr spans two lines, so the annotation kept only the first — cutting off the words that separate a back-off from a credential rotation. Verification: `scripts/tests/qwen-autofix-workflow.test.js` 179/179; yaml parses; eslint and prettier clean. Mutation-checked all three: reverting the slurp filter fails the resolve arm with exit 5 (expected 5 to be 0), reordering the outer pageInfo fails the field-order pin, and dropping the `tr` fails the folded-reason arm on the second stderr line. * docs(autofix): correct the field-order comment's mechanism (#9390 R3-1) The comment explaining why `pageInfo{hasNextPage endCursor}` order is load-bearing described the silent stop as happening with the carried `hasNextPage` "already true" from the last inner page. That cannot produce the symptom: gh's `findEndCursor` returns a cursor only `if hasNextPage`, so a carried true would keep the walk going. The real mechanism is the opposite one. The scanner carries its flags across `pageInfo` objects and breaks at the first point both have been seen; under `pageInfo{endCursor hasNextPage}` that break lands on the outer `endCursor` while `hasNextPage` still holds the last INNER page's value — almost always false, since thread comment pages rarely truncate — and the outer page's own `hasNextPage` is never read. gh returns no cursor and the walk stops after page one, exit 0 and silent. Reworded in both places the clause was copied to: the workflow comment and the field-order pin's comment in the test. No assertion, no shell, and no query text changes; `pageInfo{hasNextPage endCursor}` and the test that pins it are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(autofix): keep gh's thread-fetch stderr off a predictable WORKDIR path R4-1 (Critical, #9390): the review-thread pagination wrote gh's stderr to `${WORKDIR}/threads-fetch.err` and read it back with `tail -c 300`, both without a file-type guard. WORKDIR (`/tmp/autofix-review-<pr>`) is bind-mounted read-write into the agent docker sandbox, and the round that just finished ran branch code inside that sandbox, so the name is attacker-chosen by the time this step runs. A planted FIFO makes bash block on the O_WRONLY open before gh even execs, and the only reader is the `tail` that runs strictly after gh returns — so the step hangs to the job timeout with the push already landed, losing the report comment and the round markers. That breaks the invariant this block states for itself: a resolve failure must never fail a good push. A planted symlink instead turns the redirect into a truncate/write against the link target and the tail into a 300-byte arbitrary-file read folded into a public `:⚠️:`. Route the stderr through a fresh `mktemp` regular file instead, matching the `gh api user` checks elsewhere in this workflow, and remove it afterwards. The diagnostic is unchanged: the warning still carries gh's own reason, which is the only text separating a transient rate limit from an expired PAT. Test: plant a symlink at the old path, run the block through a failing fetch, and assert the target's bytes are neither overwritten nor folded into the annotation; plus assert the named path is not created at all. Mutation- verified — restoring the `${WORKDIR}` redirect turns the canary assertion red (`expected 'threads-fetch stub failure' to be 'CANARY-MUST-SURVIVE'`). The FIFO half cannot be written as a plain assertion because the pre-fix code hangs rather than fails; the same "named path is never opened" property defuses it. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
bc2d205d29
|
refactor(ci): simplify the review checkout self-heal back to wipe-and-retry (#9327)
* refactor(ci): simplify the review checkout self-heal back to wipe-and-retry #9220 fixed a real incident (a corrupt persisted workspace made seven review jobs fail checkout on the same missing SHAs), but eight review rounds grew the heal step from ~15 lines into ~60 lines of path-guard layers (realpath canonicalization, two trailing-slash strip loops, a denylist case, a RUNNER_WORKSPACE allowlist) plus ~450 lines of tests pinning their mutation resistance. Every removed layer defended against a mangled GITHUB_WORKSPACE. That variable is set by actions/runner; anything that could mangle it — a compromised runner, a step writing GITHUB_ENV — already executes arbitrary code on the machine and needs no wipe to do damage, so the guard cannot defend against the only actor able to trigger it. The realistic contract is the :? guard: fail loud on a dropped variable. Kept and still pinned by tests: the pool wipe idiom, the sudo fallback leg (exact argv), the never-fail exit contract with named survivors, the identical retry checkout, and the continue-on-error invariants. Also dropped with the guards: the GNU-only realpath flag and its host-probe test machinery. * test(ci): pin the runner-owned GITHUB_WORKSPACE premise before the workspace wipe Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(ci): seal the live override channels into the review wipe step * fix(ci): refuse a redirected workspace and pin the clean-wipe silence Addresses the two doudouOUC findings on the simplified heal: - The wipe now validates the filesystem OBJECT at $WS, not just the string: find -P does not descend a symlinked start, so a redirected workspace logged 'wiped for a clean retry' while deleting nothing, and the secret-bearing review step would then run through the redirection. Refuse loud on a symlink or non-directory — POSIX-only, no false-positive surface (a legitimate workspace is always a runner-created plain directory), and it pins the only sudo-escalated wipe in the pool to a validated target. - The clean-wipe silence branch was unpinned: the reviewer's minimal mutant (dropping the if/fi pair) shipped an empty-list survivor warning on every heal with the suite green. The clean-wipe test now asserts the success annotation and the absence of the survivor warning; both mutants verified red. * test(ci): seal the wipe step's surviving override channels * test(ci): seal the wipe's surviving override channels, pin its signals Addresses the open review findings on the simplified heal: - The seal's premise covered declarative env, $GITHUB_ENV/$GITHUB_PATH run writes, and the pre-wipe action set, but three channels passed it unchecked: a wipe-step `shell:` or workflow/job `defaults:` wrapper re-targets the environment at exec time; SHELLOPTS rides the same bash-startup family as BASH_ENV/ENV yet sat outside the dangerous name class; and ACTIONS_ALLOW_UNSECURE_COMMANDS re-enables the legacy ::set-env:: / ::add-path:: spellings the run-text scan did not match. Each channel was reproduced green against the old seal (mutant probe) and now turns it red. - The both-legs-fail test now also pins the else-branch "could not wipe" warning, and a dedicated test pins the `[ ! -d ]` refusal for a nonexistent workspace — the plain-file test alone still passes a guard mutated to `[ -f ]`. - The non-sudo wipe leg keeps its stderr: the 2>/dev/null discarded exactly the diagnostics oncall needs when the wipe fails, and the sudo leg already ran unsuppressed. * fix(ci): refuse workspace wipe through symlinked path components (#9327) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
846fc05461
|
feat(ci): post autofix failure-path handoff comments bilingually (#9386)
* docs(autofix): design bilingual failure-path handoff comments Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(ci): post autofix failure-path handoff comments bilingually Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): pin bilingual handoff sanitization by content, escape withdraw excerpt Address review R1-1/R1-2/R1-3: - Escape + iconv the issue-lane withdraw failure.md excerpt, the one publish site without `<!--` escaping: a failure.md quoting an HTML comment whose closer sits past the 1500-byte cut opened an unterminated comment that swallowed the new 中文说明 block (R1-3). - Widen the escape-site count test to the multi-`-e` sed form (9 -> 12 sites) and pin the full zh sanitization pipeline per-site on both lanes; dropping the `<!--` expression from either zh site or a tag substitution from the withdraw site now fails (R1-1). - Pin EN/ZH correspondence for every non-empty assignment site of HEADLINE/CAUSE/LAST_FIX/GATE_CLAUSE/IDLE_CLAUSE/REMEDY: count-only pins let a swapped adjacent HEADLINE_ZH pair pass all tests (R1-2). All four mutation witnesses from the review now fail the suite (verified locally: probe each mutation, expect red, restore). * fix(ci): close the bilingual handoff review gaps (R2/R3) Workflow fixes: - Neutralize :: in the issue-lane run-log dump loop (agent-written files on step stdout parse as workflow commands; the PR-lane twin already did this) — R2-1. - Extend the wrapper-defense substitutions (<details, </details, <summary) to the three excerpt sites that only escaped <!-- and now sit above the new 中文说明 wrapper: API_ERROR_DETAIL (flows into HEADLINE_ZH inside the wrapper) — R2-3; the PR-lane DETAIL_FILE excerpt (address-summary/no-action files are mandated to END with their own <details> tail, so a cut-straddling tail leaves a live severed opener) — R2-4; the withdraw failure.md excerpt — R3-1. - The withdraw comment's 中文说明 block now renders unconditionally with a translated REASON (REASON_ZH per branch), mirroring the PR-lane headline floor: crash shapes where run-agent.mjs writes failure.md itself no longer degrade to zero Chinese — R3-2. Accepted and documented (design doc §5): fence-token severance across the byte cut — render-only, markers parse raw, and a balancing heuristic stays wrong when the cut lands mid-closer — R2-2. Test pins (each mutation-verified locally): branch-selected zh labels — R2-5; zh gate-note text + condition + position — R2-6; failure.zh.md membership in all four dump loops plus the issue-lane :: sed — R2-8; the ZH_DETAIL guard — R2-9; full-line rm -f pins on the three pre-agent cleanup sites — R2-10; the BODY append shape — R3-3; wrapper internal ordering — R3-4. Design doc §2 reconciled with §5 on the no-detail fallback sentence — R2-7. * fix(ci): close the R4 review gaps (case-insensitive tag defense, pin gaps) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
f0dcdfc157
|
feat(triage): add a deterministic flakiness gate to sandboxed verification (#9130)
* feat(triage): add a deterministic flakiness gate to sandboxed verification Closes #9125. PR #9086's ~50% mtime-assertion flake passed every automated layer because each executed the changed tests exactly once — a coin flip a single green run cannot distinguish from health. The gate re-runs the PR's added/modified unit-test files N times (default 5, vars.QWEN_VERIFY_FLAKE_ROUNDS to override, clamped to 2..10) through the same entry points CI uses and compares outcomes per group across rounds. Design constraints, each pinned by a workflow test: - One-way authority: 'flaky' demotes the published headline (even a trusted agent merge-ready); no gate value can raise or soften one. The gate runs the PR's own test code, so it can always be neutered — but a gate that can only demote is not worth forging. - Divergence-only signal: a group failing identically every round is deterministic (CI owns it) and an environment-sensitive suite must not false-positive here; both report informationally, never demote. - Fail open: the gate is not under -e and every terminal path exits 0 — a gate bug reports verdict 'error' instead of taking down the verify lane. - Honest file list: recorded from HEAD^1..HEAD before install/build hands the workspace (and .git) to PR lifecycle code; the gate consumes the root-owned recorded list and never re-derives the diff. - Untrusted text stays out of outputs: summaries are fixed text plus counters; PR-controlled paths live in flake-gate.log, embedded through the publisher's escaping emit_block. Job timeout raised 150 -> 175 for the gate's ~25m worst case (15m round budget checked before each invocation + one 10m-capped in-flight run). * fix(triage): survive the runner wrapper's -e, per-file gate granularity, hardened log staging Round-1 review + sandboxed-verify feedback, all seven findings: - set +e after set -uo pipefail: the runner wraps every run: block in 'bash -e -o pipefail' and set -uo does NOT clear that inherited -e, so the first failing test invocation killed the step — fail-open inverted to fail-closed for exactly the flaky/consistent-fail populations the gate classifies (verify cells C/D). An EXIT trap additionally converts any abnormal ending (set -u death) into the fixed 'error' verdict. - Per-FILE groups: one runner invocation per changed test file, so a consistently failing file can no longer mask another file's run-to-run divergence behind a shared exit bit. - Owning-package resolution: nearest ancestor package.json (nested workspaces like packages/channels/base are entered themselves) plus a vitest-config probe; unsupported runner families (packages/desktop's bun test) and */e2e/* specs are logged out-of-scope instead of being mis-run as permanent consistent-fail noise. - Operands are ./-prefixed before %q, so a checked-in filename beginning with '-' (e.g. --config=x) can never be parsed as a runner option. - Log staging moved to a dedicated always() root step after the agent exits — the last write to verify-results/flake-gate.log — and the publisher pins that exact path instead of find|sort|head, so an early agent abort cannot lose the matrix and agent-era PR code (which owns a chowned verify-results) cannot control or shadow what is embedded. - Detection math corrected: N=5 catches a 50/50 flake with ~94% (1 - 2*(1/2)^5), not ~97% — all-pass and all-fail rounds both miss. - New behavioral suite executes the extracted gate and publisher fragments under the production wrapper itself (bash --noprofile --norc -e -o pipefail) with scripted per-file P/F sequences: pass, flaky-next-to-consistent-fail, consistent-fail, missing-list error, out-of-scope n/a, nested-package + leading-dash operand, and the seven-value one-way demotion — closing the structural blindness where YAML-string tests stayed green while the shipped behavior regressed. * fix(triage): isolate flake-gate rounds, classify infra exits, widen runner resolution (#9130) Review-round fixes for the deterministic flakiness gate: - Reset shared state between rounds (restore tracked files, tear down test-user processes, fresh per-invocation TMPDIR) so a deterministic test cannot fail on its own residue and fake a divergence (R1-8). - Classify timeout/signal exits (124, 128+N) as infrastructure, not F marks, and report the informational timeout verdict instead of a fake flaky (R2-3/R3-2). - Resolve the vitest runner by owning package + vitest's real config list (vite.config.* included), keyed on the package lookup instead of a packages/* prefix, so webui and integrations workspaces are re-run instead of skipped (R2-2/R3-3). - Narrow the scripts/tests arm to the pinned config's *.test.{js,ts} include set so admitted-but-rejected files are skipped, not mis-run into a bogus consistent-fail (R3-11). - Harden the gate-log staging: kill leftover build-user processes, and remove a planted destination entry before copying so a FIFO/symlink can neither hang the copy nor redirect it (R1-5). - Cap the embedded gate log at 10000 chars to keep the assembled comment under GitHub's 65,536-char limit (R3-4). - Record changed files with core.quotePath=false so non-ASCII test filenames are not silently dropped (R3-5). - Behavioral tests: hermetic timeout/pkill stubs (the suite no longer depends on GNU coreutils, fixing the macOS red), infra-exit and round-reset scenarios, trap-abort fail-open, node --test arm, FLAKE_ROUNDS clamping, fixed-shape summary, record-step shape pins. * test(triage): follow the widened special-file strip into the vitest twin pins Commit |
||
|
|
19a4b973eb
|
fix(ci): back-port the checkout-heal wipe guard to the triage and serve-ab wipes (#9277)
* fix(ci): back-port the checkout-heal wipe guard to the triage and serve-ab wipes The "empty the workspace, keep the directory" idiom exists in three copies; only the review workflow's copy received the #9220 hardening (canonicalization, trailing-slash strip, RUNNER_WORKSPACE allowlist). Measured on main for #9265, the two triage guards let non-canonical spellings of the guarded roots through (/home/, /home/., //usr, /root/, /var/ all reached the rm), and serve-ab's wipe had no guard at all — even `/home` or an empty string arrived at `find … -exec rm -rf`. Port the reference guard to all three sites, keeping each site's exit contract: triage fails loud both before and after external code, serve-ab stays bare under the job's `-eo pipefail` so an unclearable workspace fails before either checkout builds on top of the leftovers. Pin each ported copy with its own tests: bad-path batteries under an rm recorder (the destructive primitive cannot fire under any edit), an allowlist-escaping `..` case gated on a GNU-realpath host probe (the lesson from 90fa6bb4), a realpath-absent trailing-slash RUNNER_WORKSPACE case, and text pins on the ported layers. Every pin was mutation-verified red against a deletion of the layer it guards. * test(ci): pin guarded serve wipe * fix(ci): close wipe guard fallback gaps * fix(ci): fail closed without realpath * fix(ci): keep wipe guards portable * test(ci): pin wipe-guard RWS layers and unmask the pre-run battery - run the rewritten pre-run sweep battery under -e -o pipefail so a failing sweep can no longer report success (bare bash -c masked it) - pin the RWS '..' refusal and degenerate-root refusal text in all copies, and add RUNNER_WORKSPACE='/' exec cases to both copy suites - exercise both pre-run and post-run copies in the realpath-absent refusal test - replace the '..' escape vector with a symlink escape that only the realpath line can refuse, and correct the mutant-outcome comments - add the serve-ab wipe-before-checkouts ordering pin from the sister suite and a happy-path RWS canonicalization pin * test(ci): correct wipe-guard mutant-outcome comments for find -P The symlink-escape comments claimed that with the WS realpath line deleted, find reaches rm through the link target. GNU find's default -P mode does not descend symlink operands: the mutant passes every guard, wipes nothing, and exits 0, so only the non-zero-status assertion catches it — the rm-log assertion passes vacuously. Reword both twin comments (R5-1). --------- Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com> |
||
|
|
a4a3850fe5
|
fix(ci): make autofix busy detection fail closed and mark dispatched PRs (#9329)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
* fix(ci): drop pull_request_review events on closed PRs at the route gate Reviews on merged/closed PRs have nothing to address, yet each one started an autofix run that spun up a runner only to exit no-op. Observed 2026-08-16: 24+ finding-reply reviews on merged #9222 and 26 runs on merged #9189 within minutes (issue #9296). Add a PR open-state clause to the route prefilter; the scheduled scan remains the backstop, and address-time revalidation already drops targets whose PR closed after dispatch. * fix(ci): make autofix busy detection fail closed and mark dispatched PRs Silent API failures in the busy-PR enumeration re-dispatched PRs whose address legs were already running or queued (issue #9296): each duplicate burned one build-cli (~5 min) before cancelling a queued sibling leg through the per-PR group's latest-wins queue. - Any enumeration failure (run list or per-run jobs view) now empties the scan's candidate set for this pass; a forced dispatch keeps its explicit-override semantics. - Stamp a pending commit-status marker (qwen-autofix/dispatch-pending) on the PR head at dispatch and treat it as busy while fresher than 30 minutes; the address leg re-stamps it success on checkout. This covers the scan->build-cli window where the matrix leg does not exist in the live-run jobs view yet. Commit statuses only: the check-run creation API needs a GitHub App, and the workflow authenticates with a PAT. Refs #9296 * fix(ci): keep the dispatch-pending marker from blocking past its TTL Exempt the marker's status context from the HAS_PENDING_CHECKS gate (a stranded marker otherwise blocked the PR for up to ~330 minutes, not the documented 30-minute TTL), release it on the address-time discard path, guard every status write same-repo and dry-run, narrow the fail-closed carve-out to explicit workflow_dispatch dispatches, emit enum_failed so an emptied candidate set cannot flip the scheduled issue phase on, and carry the enumeration error tail in the fail-closed warning. Pin all of it behaviorally in the workflow contract tests. |
||
|
|
d9d210eb7a
|
feat(autofix): seed the takeover round counter with /takeover from N (#9321)
* feat(autofix): seed the takeover round counter with `/takeover from N` Taking over a PR that has already been through several review rounds restarted the Critical-only brake from zero: the round counter is window-scoped, and engaging takeover opens a fresh window, so a PR that spent nine human rounds getting to "almost mergeable" got five more suggestion-capable rounds the moment it was managed — the diff grew on nice-to-haves exactly where it should have been converging. `@qwen-code /takeover from N` now seeds the window's counter at N, so CRITICAL_ONLY_AFTER_ROUND is reached in the remainder rather than a full fresh five. This is the one parameterized command form: the literal prefix must still match TAKEOVER_COMMAND byte-for-byte, the tail is a bounded 1-2 digit integer, and the captured value reaches nothing but an integer comparison. Everything else — a prefixed body, a `stop from N` hybrid, a substitution payload — still fails closed. The seed rides as its own `<!-- autofix-round-start N -->` marker on a separate line of the engage ack, never as a field inside `<!-- takeover-ack engaged -->`. That literal is matched with jq contains(), closing `-->` included, at seven read sites — four here and three in the fleet shepherd's paused/resume detector — so an inline field would silently break all of them: the window key would fall back to an older ack and the shepherd would age out a PR that was just re-armed. Same shape as the existing autofix-redcheck marker. Both round readers fall back to the seed instead of a hardcoded 0, read it by created_at equality against the window key (so a superseded window's seed cannot leak forward), and clamp it strictly below the effective cap so a seed can never park a PR at its round cap on the very round it is taken over. The seed is window-scoped like every other census: `/retry` or a bare re-takeover returns the counter to 0. Both engage acks and the Critical-only audit record now name the seed when there is one — otherwise the ack reports "round 4/100" on its first managed round, and the audit record claims five completed rounds on a PR the loop has run twice. The growth brake is deliberately not seeded: its baseline anchors at the window's first measured round, and a pre-takeover baseline is not recoverable, so growth stays measured from engagement. * fix(autofix): address the R1 review findings on the takeover round seed Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(autofix): address the R2 review findings on the takeover round seed * fix(autofix): address the R3 review findings on the takeover round seed --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
ba2d512497
|
feat: chain Terminal-Bench release evaluation (#9120)
* feat: chain terminal bench release evaluation * fix: gate terminal bench on release publication * test: add one-task terminal bench release smoke * test: run one SWE and TB task for release smoke tags * fix: publish EAS cache under runtime lookup tag * fix: pin smoke releases to published Qwen version * fix: require explicit prerelease Qwen reference * ci: cover terminal bench manifest and harden release inputs |
||
|
|
5492009bb2
|
fix(ci): drop pull_request_review events on closed PRs at the route gate (#9299)
Reviews on merged/closed PRs have nothing to address, yet each one started an autofix run that spun up a runner only to exit no-op. Observed 2026-08-16: 24+ finding-reply reviews on merged #9222 and 26 runs on merged #9189 within minutes (issue #9296). Add a PR open-state clause to the route prefilter; the scheduled scan remains the backstop, and address-time revalidation already drops targets whose PR closed after dispatch. |
||
|
|
d8b5d532c6
|
fix(ci): minimize new spam comments on creation (#9266)
* fix(ci): run spam cleanup every five minutes * fix(ci): minimize spam comments on creation * fix(ci): tolerate deleted spam comments * fix(ci): preserve live spam comments * test(ci): evaluate spam minimizer jq filter * fix(ci): handle deleted spam comments * test(ci): pin spam minimizer guards * test(ci): pin spam guard parentheses |
||
|
|
f9e6b67aa7
|
fix(ci): stop triaging the autofix bot's own deferred-finding tracking issues (#9264) (#9271)
* fix(ci): stop triaging the autofix bot's own deferred-finding tracking issues (#9264) Every PR that defers findings for the first time opens a tracking issue upserted by the autofix bot, and the issues trigger (opened/edited/reopened) ran a full triage agent on that bookkeeping issue per deferral — the authorize gate exempts the issues path as read-only, so nothing stopped it. Condition the triage job's issues clause on the creator not being the autofix bot (the same vars.AUTOFIX_BOT_LOGIN || 'qwen-code-dev-bot' identity qwen-autofix.yml upserts under), and route bot-created issues runs to a per-run concurrency group: GitHub evaluates concurrency before the job if, so a run left in the shared per-number group would still cancel an in-progress triage of the same issue before its own skip is evaluated. Pins: the issues-clause guard, the group routing, and the cross-workflow identity sync, all on the parsed document. * test(ci): pin triage issue guard connectors * test(ci): harden triage bot guard pins --------- Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com> |
||
|
|
f7f78fab4a
|
fix(ci): force-push release branch so retries replace failed attempts (#9076) (#9082)
* fix(ci): force-push release branch so retries replace failed attempts (#9076) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): re-validate the release version before force-pushing (#9076) prepare's doesVersionExist check runs minutes to hours before publish pushes (validation jobs and the production-release approval gate sit in between), and --force removed the non-fast-forward rejection that used to serialize the push itself. Concurrent same-version runs could therefore diverge the npm artifacts, the git tag, and main. Serialize publish per release tag and re-validate the unshipped invariant — every published package, the tag, and the release — immediately before the push; pin all three invariants in the workflow tests. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): unify the push-time release guard and key concurrency by dry-run (#9076) * fix(ci): fail closed on push-time release probes and test the CLI seam (#9076) * fix(ci): clarify push-time release refusals and keep benign ones out of autofix (#9076) The push-time guard refused retries after a partial npm publish without saying where the version had shipped or how to recover, and every refusal failed the publish job into notify_failure, filing a "Release Failed" issue and dispatching the autofix agent against releases that did not fail. - Scan all published packages in strict mode and name every shipped location in the refusal (npm packages, origin tag, GitHub release) with partial-publish recovery guidance; a decisive hit ends the check so a flaky later probe cannot mask the refusal with a probe error. - Give the guard distinct exit codes: 3 = already shipped (decisive, benign), 2 = probe or usage failure. Exit 1 is reserved for uncaught node errors so a crash can never masquerade as the benign marker. The push step marks exit-3 refusals via the version_refusal job output, and notify_failure skips its issue + autofix dispatch for exactly that failure while genuine failures still notify. - Cover runCli's default dispatch (prepare's path), the exit-code contract, and the process.exit wiring end to end. * fix(ci): fail closed when the release ref predates the push-time guard (#9076) * fix(ci): keep refusals decisive after shipped hits and skip the POSIX-only test on win32 (#9076) * fix(ci): write push-time guard error annotations to stdout (#9076) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
c031cc6279
|
fix(ci): self-heal failed checkouts on the reused review runners (#9220)
* fix(ci): self-heal failed checkouts on the reused review runners A checkout failure on the self-hosted review pool was terminal: either a transient network drop mid-fetch (curl 92 / early EOF), or a corrupt persisted workspace whose refs claim objects missing from its object store, after which every fetch dies in negotiation with 'remote did not send all necessary objects'. ecs-qwen-runner-64c-23 stayed in that state for two days (2026-08-13..15), failing seven review jobs on the same missing SHAs. Make the first checkout continue-on-error; on failure wipe the whole workspace (not just .git) and retry the identical checkout once. The workspace is disposable — later steps reinstall deps and tools. * fix(ci): heal with the pool wipe idiom and pin the checkout guardrails (#9220) * fix(ci): pin the heal chain's sudo leg, path guard, and survivor signal Addresses the 16:40 review round on the checkout self-heal: - The wipe-failure test leaned on the real sudo, so it covered a different branch per lane; replace it with a PATH-stubbed sudo that forces both legs to fail hermetically, and pin the survivors left in place plus their oncall-visible warning. - The '|| sudo -n find' escalation leg survived deletion mutants: add a stub-sudo test proving the leg actually runs when user-mode find fails (leg-deletion and '||'->'&&' mutants both verified red). - Reuse the triage idiom's suspicious-path guard before wiping. - Count post-wipe survivors and warn with the count — triage exits 1 here, but the heal chain must stay alive for the retry. - Disclose in the step comment that the sudo leg only helps pool members with passwordless sudo. * fix(ci): close the heal guard's trailing-slash hole and name wipe survivors (#9220) * fix(ci): canonicalize the heal guard's path match and allowlist the runner workspace (#9220) * fix(ci): strip the heal allowlist root's trailing slashes and pin the guard layers (#9220) * fix(ci): canonicalize the heal lock fixture and pin the WS strip loop (#9220) * fix(ci): keep the checkout-heal suite green on a BSD userland Addresses the 09:38 review round: three of the new tests assume the wipe script's `realpath -m` canonicalization actually ran, and `-m` is a GNU coreutils extension — Darwin ships FreeBSD's `realpath [-q]`, exits 1 on it, and the script's `|| printf` fallback silently keeps the raw path. The production script is unaffected (the review pool is Linux-only), but this suite is excluded on win32 alone, so it also runs on the macOS lane, where the assertions are red for a defect that cannot exist there. - Probe the host for `realpath -m` and skip the canonicalization test when it is absent, rather than skipping on `platform === 'darwin'`: the probe keeps the coverage on a Mac with coreutils on PATH and still skips on any other non-GNU userland. Mutation-checked on a GNU host — deleting the canonicalization line still turns the test red. - Spell both halves of the allowlist comparison the same way in the lock fixture: it resolved its workspace with realpathSync while the runner-workspace root stayed raw, so on a symlinked tmpdir (macOS /var -> /private/var) the two sat on opposite sides of the link, the guard refused, and the wipe helper threw before any assertion ran. Canonicalizing the root keeps both tests running everywhere instead of leaning on the GNU-only flag to reconcile them. - Record in the step comment that `-m` is GNU-only and that off-GNU the guard degrades to the strip loop and the allowlist. Verified by simulating a BSD userland (a PATH-fronted realpath that rejects -m): the suite goes from 1 failed / 12 passed to 12 passed on the CI lane's environment, and from 3 failed / 10 passed to 12 passed with a symlinked TMPDIR, matching the two failure shapes reported. --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
b744248656
|
fix(ci): keep a fallback comment when the PR review runner dies (#9255)
* fix(ci): keep a fallback comment when the PR review runner dies A review job that dies abnormally never reaches its in-job fallback comment step: the runner worker crash in FinalizeJob on the PR #8894 run (EACCES creating under the runner home directory) left the PR with no review and no explanation. - Probe write access to $HOME, $RUNNER_TEMP and the runner root at job start, repair single-directory ownership with the existing sudo pattern, and fail fast with a clear message when repair is impossible instead of burning the review budget to die at finalize. - Add a fallback-comment job on an ephemeral hosted runner that posts the retry guidance whenever review-pr fails. It derives the PR number from the event payload (dead job outputs do not survive a crash) and dedupes on a qwen-review-fallback comment marker plus this run's URL, so the in-job step, the ack comment, and re-runs never double-post. * fix(ci): harden the PR review fallback comment (#9255) Review-round fixes for the fallback-comment defenses: - Probe the actual runner root (three levels above the workspace, not two) and the _diag subdirectory FinalizeJob writes in; a writable parent does not prove an existing subdirectory writable. - Open the fallback gate on authorize/review-config failures too — the incident's trigger can kill those earlier self-hosted jobs first, and a failed dependency marks review-pr 'skipped', which the old gate never matched. Guarded against resolve dispatch runs, which skip review-pr by design. - Author-scope the dedup lookup (resolved dynamically like upsert-bot-comment.sh) so a planted marker cannot suppress the fallback, and fail closed with bounded retry when the lookup or the state check fails instead of fail-open toward duplicates or a green job that never posted. - Skip the stale fallback when the PR head moved, but only on pull_request_target events where the run head is comparable — comment/review runs report main's tip, and posting wins over silence when the comparison is unavailable. - Define the marker once in a workflow-level env and pin all of the above in the workflow test suite, executing the fallback step's real bash against a stubbed gh. * fix(ci): close the fallback-comment gate gaps from round-2 review (#9255) - Exclude comment-driven /resolve runs from the fallback gate: authorize runs on `@qwen-code /resolve` issue comments where github.event.inputs is empty, so the dispatch-only exclusion never fired there and a failed resolve run was misdiagnosed as a dead review recommending the wrong command. - Enumerate precheck-pr and delay-automatic-review failures in the gate: either failure marks review-pr 'skipped' (a transient API 5xx in delay's re-check step, or the fork-PR chain root dying before it posts anything), which the old gate never matched — silence, against its own "a skipped review is as unexplained as a dead one" norm. Both are 'skipped' where they do not apply, so the gate stays closed there. - Anchor the cross-job dedup on the run URL's closing paren: run ids grow digits over time, so the unanchored substring let a later run's fallback comment (id 123450) suppress an earlier run's (id 12345) re-run comment; every marker body renders the URL as [workflow logs](...runs/<id>), so the id is always followed by ')'. - Pin the previously surviving mutants in the workflow suite: the _diag probe guard polarity, the workflow_dispatch disjunction, the ephemeral-hosted-runner placement, and the fallback body's marker-link shape the anchor relies on; add executed coverage for each head-lookup partial failure and for a distinct run's fallback not suppressing this run's comment. * fix(ci): close round-3 review gaps in the fallback-comment defenses (#9255) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
a9bff6c9b8
|
feat(autofix): defer verified out-of-footprint findings to a surviving follow-up queue (#9189)
* feat(autofix): route verified out-of-footprint findings to a surviving follow-up queue
Anti-drift closure for the review loop: a finding that is REAL but whose
fix lies outside the PR's footprint previously had only wrong outcomes —
implement it (scope drift), decline it (the finding is lost when the PR
merges and nobody re-reads its threads), or push it onto a maintainer.
- SKILL gains the fourth disposition, Defer to follow-up: verified +
out-of-footprint → record {id, path, reason} in
deferred-findings.json, reply on the thread that it is deferred, leave
the thread open. Decline stays for what is not worth doing anywhere;
defer is for what is worth doing elsewhere.
- The report step upserts these into one per-PR 'Deferred review
findings' issue (marker-keyed, append-only by rc id, agent text
token-neutralized and length-capped), for BOTH pushed and no-op
outcomes. Best-effort: an upsert failure never fails a round.
Deliberately no ready-for-agent label — feeding the bot's own
deferrals back into its issue queue is a human authorization.
- deferred-findings.json rides the artifact dump and the repair
cleanup; the neutralization ledger grows to ten sites.
- Tests: shape validation (non-empty array of numeric-id items), line
building (dedupe by rc id against the existing issue body, newline
flattening, truncation), and wiring pins for both call sites.
* fix(autofix): rebuild the deferred-findings upsert per review round 1
- Extracted to a trusted staged script callable from ALL outcome paths —
the failure/handoff path persists verified findings too (a failed
round's commit dying says nothing about the findings' validity).
- Append-only durability: the tracking issue's body is written once;
every later round POSTS a comment — no read-modify-write can race a
maintainer's edits, and a failed body/comments read SKIPS the round
(never mistaken for empty history). Success is logged only when the
write call succeeded; failures say NOT persisted.
- Structured lookup: jq filtering over the real bodies (no line-joined
awk under pipefail), pull requests excluded, lookup failure skips
rather than creating duplicates.
- Dedupe is line-anchored ('- rc:<id> ' at line start, body+comments
corpus) with intra-batch unique_by; ids the round resolved in code are
excluded (a finding cannot be implemented and outstanding at once).
- Shape gate covers path (string when present); path bytes are
charset-sanitized so a crafted path cannot forge queue bullets.
- Publication-trust posture recorded: the deferred lines are the same
agent-authored trust class as every other published output — marker
neutralization, mention-free sanitized charset, length caps, and a
20-item batch cap bound the surface.
- Tests: the real script runs against a recording gh stub — create,
append+dedupe (body and comments), PR-carrying-marker exclusion,
anchored dedupe vs free-text mentions, read-fail skip,
resolved-exclusion, shape-gate loudness, write-fail honesty, and
forged-path sanitization; the neutralization ledger returns to nine
workflow sites with the script-side tenth pinned in place.
* fix(autofix): harden deferred-findings upsert per review round 2
- Pass the known-id corpus to jq via --rawfile: a large corpus in one
--arg argv element hits Linux MAX_ARG_STRLEN and the swallowed exec
failure would silently drop the round's deferrals.
- Digest-gate the staged upsert script: record upsert_sha256 at stage
time (expression context) and verify before each of the three
invocations; RUNNER_TEMP is agent-writable in between. A mismatch
skips persistence, never the round.
- Add the gh hygiene preamble (GH_HOST pin, GH_TOKEN unset, fresh
GH_CONFIG_DIR) to the review-address failure/handoff report step —
the one PAT-bearing gh step that lacked it.
- Query the tracking-issue lookup with state=all so a maintainer-closed
issue is appended to instead of forking a duplicate.
- Enforce integer positive finding ids in the shape gate (a float id's
dot is a regex wildcard in the anchored dedupe and never
index()-matches resolved ids).
- Clip the 20-item batch loudly and qualify success messages with
kept/total counts instead of claiming full persistence.
- Tests: digest + hygiene wiring pins; stub knobs for list/comments
fetch failures; append-write failure, multiline-reason flattening,
bad-id, loud-cap, and state=all cases.
* fix(autofix): close bash/transport channel gaps and failure-path gate holes (review round 3)
- Sweep BASH_ENV/ENV and imported BASH_FUNC_*%% functions plus proxy
(HTTPS_PROXY/HTTP_PROXY/ALL_PROXY + lowercase) and SSL_CERT_FILE/DIR
at all four gh-hygiene sites: both families are GITHUB_ENV-plantable
and bypass the TRUSTED_PATH pin (child-bash startup) or reroute/
decrypt PAT-bearing HTTPS. Also de-shadow gate-critical names and
hash -r, since a planted BASH_ENV runs before the step body.
- Pin PATH to the staged trusted value (guarded for pre-stage crashes)
and drop the loader trio in the failure/handoff report step — its
digest gate previously ran under ambient PATH/LD_PRELOAD.
- Failure-path upsert: skip with a plain notice when stage never ran
(empty digest is not a tamper alarm), and verify the PAT's bot
identity before writing (POST_HANDOFF's check is skipped on the
fixed/noop-outcome path); correct the guard comment that claimed
parity with the handoff guards.
- Fold the twice-pasted digest-gate + upsert block in 'Push and report'
into a step-local run_deferred_upsert(), matching the
resolve_and_reply_threads convention.
- Dedupe corpus reads bot-authored comments only, so a third party
commenting on the public tracking issue cannot suppress a finding.
- Tests: hygiene sweep pins ordered before the first gh call; placement
assertions (function defined once, called after both resolve arms;
failure invocation inside the DRY_RUN/STALE/token guard slice);
digest/identity/notice pins; behavioral cases for foreign-author
suppression, intra-batch duplicate ids, markerless-issue create path,
creator/marker anchors, and marker neutralization through the append
path.
* fix(autofix): isolate deferred-upsert in a clean env -i child, drop the unsound in-shell sweep (review round 4)
Round 4 (R4-1, five Criticals of one class) showed the in-shell
BASH_FUNC/proxy denylist sweep the prior round added is unsound: it
bootstraps trust from the very shell namespace it sanitizes, and a
planted BASH_FUNC_env%%/unset%%/command%%, an expand_aliases alias, a
readonly -f shadow or a DEBUG trap each defeat it — ending in the
staged upsert script executing with CI_DEV_BOT_PAT in env.
- Replace it with sound isolation: both upsert sites (run_deferred_upsert
in 'Push and report', and the failure/handoff path) run the digest
gate, the PAT identity check and the staged script in a fresh
'/usr/bin/env -i … bash --norc -c' child. /usr/bin/env is invoked by
absolute path — bash never does function/alias lookup on a
slash-bearing word, so a planted BASH_FUNC_env%% cannot intercept it —
and env -i drops every BASH_FUNC_*, BASH_ENV, SHELLOPTS, alias and
trap before any gated work. GH_CONFIG_DIR is minted inside the clean
child (its mktemp cannot be shadowed there), closing the mktemp-shadow
hole in the failure step's preamble.
- Remove the sweep and the in-step gh/PATH preamble the prior round
added to all four PAT gh steps; the three pre-existing steps revert to
their prior posture. Hardening the pre-existing PAT gh calls
(handoff/report comment, push, publish) against BASH_FUNC/transport
plants is noted as separate, out of this feature's scope.
- Script: accept a contract-valid empty array as a clean no-op instead
of a false 'malformed' alarm; 'set +C' so a planted read-only
SHELLOPTS=noclobber cannot silently empty the dedupe corpus (belt to
the env -i child that already drops SHELLOPTS).
- Tests: replace the sweep pins with clean-child pins (absolute-path
env -i at both sites, GH_CONFIG_DIR/PATH inside the child, failure
launch inside the guard slice); add no-findings exit-0, empty-array
no-op and set +C cases. Behavioral probe: a fully tainted parent
(BASH_FUNC/alias/BASH_ENV plants) cannot reach into the env -i child.
* fix(autofix): strip LD_* before the env -i upsert child; tighten id gate, mktemp/cap guards (review round 5)
- R5-1 (Critical): LD_PRELOAD/LD_AUDIT/LD_LIBRARY_PATH is the one channel
env -i cannot block — ld.so maps a planted library into /usr/bin/env
itself at execve, before -i wipes anything. Neutralize it with a
command-prefix assignment (LD_PRELOAD= LD_AUDIT= LD_LIBRARY_PATH=)
before /usr/bin/env at both upsert launch sites: a pure shell
parameter assignment no BASH_FUNC can shadow, applied to env's own
environment. Probe: a parent LD_PRELOAD=/evil.so no longer reaches the
env binary or the child.
- R5-3: shape gate rejects integer-valued floats jq renders in
scientific notation past 2^53 (1e21 -> "1E+21") — the '+' is a
regex-active byte in the anchored dedupe. Add a <2^53 bound and a
tostring plain-digits belt.
- R5-4: guard mktemp failure (a known /tmp-exhaustion CI state) so it
warns and skips instead of a silent exit 0 that violates the header
contract.
- R5-2: the cap warning no longer promises 're-defer in a later round'
(impossible — the eval-watermark filters evaluated feedback out
permanently); it names the dropped bullets for a maintainer.
- Tests: LD prefix, GH_HOST-in-child and gate/exec ordering pins; tie
the two near-verbatim clean-child bodies together (R5-6); source pins
for both --paginate sites (R5-5); if-!-echo gate-condition pin (R5-9);
failure step added to the sweep-removal regression loop (R5-11);
behavioral cases for sci-notation id, mktemp failure, prefix-colliding
dedupe boundary (R5-10) and the reworded cap.
* fix(autofix): defuse mentions in deferred bullets, verify child liveness (review round 6)
- R6-9 (Critical): the reason is agent-influenced prose published under
the bot identity, so a raw @ fired real mentions from the tracking
issue. Defuse before rendering: @ gets a trailing ZWSP, and the entity
spellings GitHub decodes BEFORE its mention filter (@ @
@ @) get their & escaped. Both measured inert against the
real renderer; \@ and leaving & alone are not. Paths were already
charset-reduced. Byte-exact probe: 2/2 @ defused, all four entity
spellings escaped, no raw spelling survives.
- R6-8: LD_* cannot be enumerated — LD_TRACE_LOADED_OBJECTS is
presence-tested, so even the empty prefix assignment leaves trace mode
on and /usr/bin/env prints its libs and exits 0 without ever running
the child (probed). Verify the RESULT instead: the child prints a
liveness sentinel first and its absence is reported. The inspection
uses bash builtins only — an external grep would itself
print-and-exit-0 under trace mode, neutering the check (measured: the
first grep-based attempt failed exactly this way).
- R6-4: guard the child's own GH_CONFIG_DIR mktemp; an empty value falls
back to the shared ~/.config/gh.
- R6-6: a line-builder (jq/sed) failure warns instead of exiting
silently as 'nothing new' — the last path skipping the header
contract.
- R6-3: write-failure warnings say the findings are LOST (watermark-
gated, never retried) and name the bullets, matching the round-5 cap
wording fix.
- Tests: allow-list entry pins (R6-7), sentinel/builtin-inspection and
child-mktemp pins, identity-check ordering (R6-5), plus behavioral
cases for mention defusing, reason-type and id-0 gate clauses (R6-2)
and line-builder failure.
* fix(autofix): defer findings from all three feedback sources; carry them across repair (review round 7)
- R7-1 (Critical): only inline comments carried an id in feedback.md, so
a verified out-of-footprint finding raised in a review body or an
issue-level PR comment could not be deferred and was lost at merge.
Feedback now renders [rv:<id>] and [ic:<id>] alongside the existing
[rc:<id>], the record takes an optional "source", and bullets anchor
under a per-source prefix so id spaces cannot collide. The resolved-id
exclusion stays inline-only (that is what resolved-comments.txt holds).
SKILL documents all three sources and that only inline findings have a
thread to reply on.
- R7-2 (Critical): every abort path said "skipping ... this round",
implying a retry that cannot happen (the eval watermark filters this
round's feedback out of every later round and the next reset wipes the
file). All six now report the findings as LOST and dump the raw
deferrals for manual recovery, with :: neutralized — the dump is
agent-influenced and a raw :: at line start is a workflow command.
- R7-3 (Critical): 'Repair deterministic rejection' deleted
deferred-findings.json before any upsert site ran, so run 1's
deferrals died in a repaired round. It now carries them into a sidecar
the upsert unions in (merging if an earlier repair left one), and the
sidecar rides the artifact dump. A/B probe: base arm loses run 1's
deferral, fix arm persists both.
- R7-4: a present-but-non-string path (false) passed the gate because //
treats false as absent; the gate now tests .path|type directly.
- R7-5: LD_PROFILE/LD_PROFILE_OUTPUT/LD_DEBUG/LD_DEBUG_OUTPUT are
non-blocking loader file-write channels the liveness sentinel cannot
catch, so they join the command-prefix neutralization (probed inert
when empty).
- Tests: per-source anchoring and dedupe, unknown-source rejection,
path:false, carry-only and carry-merge cases, LOST dump with ::
neutralization, plus wiring pins for the carry, the feedback ids and
the extended LD prefix.
* perf(autofix): bound the deferred-issue lookup and name gh failure causes
Clears the two backlog items the review has re-raised every round since
round 2 (R2-6, R2-10); both live in the file this PR adds.
- R2-6: the tracking-issue lookup ran a full --paginate over every issue
the bot has ever opened, on every round that defers anything, keeping
only the first match. It now walks newest-first pages and stops at the
first marker match: one request in the common case, a short page ends
the scan (corpus exhausted -> create), and a 10-page cap bounds the
worst case. Reaching the cap without a match SKIPS rather than opening
a second tracking issue for the same PR.
- R2-10: every gh call discarded stderr, so a rate limit, an expired PAT,
a transport error and a 404 rendered identically in the feature's only
signal. All five calls now capture stderr to one sink and the warnings
name the cause, :: neutralized like every other echoed API/agent
content.
Measured on the shipped script with a recording gh stub: first-page hit
1 request, empty corpus 1 request + create, page-2 hit 2 requests, cap
10 requests + skip, and the 403/401 bodies reaching the warning text.
* test(autofix): close the pin gaps the round-8 mutation sweep found
Round 8 raised 15 findings, none Critical: two behavioural, the rest
test pins the reviewer proved vacuous by mutation.
- R8-3: the carry-merge failure branch discarded THIS run's deferrals
with no raw dump — the one loss path in the feature without recovery
output. It now prints the set (:: neutralized) before deleting it.
- R8-1/2/5/6/7/8/9/10/11/12/13/14/15: pins that survived their own
mutations. Notably: the negative sweep pins now assert the PROPERTY
(no BASH_FUNC / unset -f / hash -r in non-comment lines) instead of
round 3's exact spelling; allow-list entries must sit inside the
env -i argument list, not merely somewhere in the step; the identity
check is pinned whole so a fail-OPEN mutation cannot pass; the repair
cleanup's deletion pin is spelling-independent and allows exactly the
one delete that follows a merge; the staging pins are scoped to the
stage step with cp ordered before the digest record; and the script's
<!-- escape site gets the count+canonical treatment its workflow
siblings already had.
Each new pin was mutation-verified: 8/8 injected regressions turn the
suite red (differently-spelled sweep, relocated allow-list entry,
deleted GH_CONFIG_DIR export, fail-open identity check, re-added
cleanup deletion, deleted re-print loops, ascending lookup order,
no-op sed spelling).
* fix(autofix): close the upsert TOCTOU and the per-id deferral collapse (review round 9)
- R9-1 (Critical): the digest gate was check-then-use — sha256sum read
the staged path and bash re-opened it, two opens of a path this PR
itself calls agent-writable. The child now reads the script ONCE and
runs those exact bytes (bash -c "$UPSERT_SRC"), so the bytes hashed
are the bytes executed. A/B against an inotify-driven same-user
rename(2) watcher: old shape 20/20 payload executions with the gate
never firing, new shape 0/20 (legit 20/20, control 20/20).
- R9-2 (Critical): unique_by([source, id]) collapsed DISTINCT findings
sharing one review-body or issue-comment id — the two sources this PR
adds — and reported success while losing them. Dedupe identity and the
corpus check are now per rendered line for those sources (inline
comments keep id identity and the cross-round anchor). Probe: two
findings under one review id now both persist ("3 of 3 new"),
byte-identical records still collapse, inline behaviour unchanged.
- R8-4 (re-raised): fixed structurally instead of by the suggested
prefix entry, which is a no-op — LD_SHOW_AUXV is presence-tested, so
an empty assignment still dumps 22 auxv lines (measured; env -u does
not help either). Loader side channels write to the LAUNCH process's
stdout, so that stdout is discarded and the child logs to a private
file; path and read-back are fork-free ($$ expansion, $(<file)) so a
polluted parent cannot leak noise into the value. Measured: with
LD_SHOW_AUXV planted the log holds 0 auxv lines and the child still
runs; with LD_TRACE planted the sentinel is absent and the warning
fires.
- R9-3/4/5: the manual-recovery dumps now say when they truncate, and
name the full byte count.
- R9-11: the artifact dump neutralizes :: in the agent-written files it
prints, like every other echo of them.
- Tests: pins for the single-read exec, the private log, the fork-free
parent handling, the identity check's ENFORCEMENT (R9-9), an
allow-list that must hold ONLY the sanctioned entries (R9-10), the
sentinel comparison inside the re-print loop (R9-13), and R9-2's
multi-finding cases. Also fixes an argList slice that anchored on a
comment mention and silently widened to the whole step.
* fix(autofix): keep a poisoned carry from sinking the round; clear the pin backlog
Clears the eleven items carried from round 9.
- R9-18: a carried sidecar that PARSES but fails the shape gate used to
abort this round's valid deferrals too — asymmetric with the
unparseable-carry branch, which persists this round only. The gate is
now a function applied to the merged set, with a retry on this round's
own file; the carry is dumped and named LOST. Measured on the real
script: valid own + gate-invalid carry -> own persisted, carry dumped;
valid own + unparseable carry -> own persisted; invalid own -> loud
total abort, nothing written.
- R9-20 / R9-7: the union argument order IS the freshness guarantee
(jq unique_by keeps first-of-group in original order), pinned at both
sites; measured: a duplicate id keeps this round's text, not the
carried one.
- R9-3/4/5 follow-up: the three truncation dumps became one dump_file
helper instead of a fourth copy.
- Pins: --paginate anchored to the comments call (R9-8), the stale
two-sites comment corrected (R9-6), the no-in-shell-sweep property
widened past function-unset spellings to alias/trap/proxy forms
(R9-12), the repair cleanup's deletion pin extended to rm -rf and to
any second multi-line list (R9-16), runUpsert's spawnSync bounded like
its sibling harness (R9-17), the explicit review_comment spelling
covered (R9-19), and the 20-item cap's survivor set pinned from a
MEASURED run whose sort order and input order disagree (R9-14) — the
four records written first are the ones dropped.
Mutation-verified 6/6: relocating --paginate, an alias-form sweep, an
rm -rf deletion, either union order swapped, and dropping the
poisoned-carry fallback all turn the suite red.
* test(autofix): widen the denylist-sweep guards past their word-boundary hole
`\btrap -\b` cannot match `trap - ERR EXIT`: the boundary sits between
`-` and a space, both non-word characters. The same hole was in two
sibling guards — `\bunset -f\b` misses `unset -fv name`, and
`\bhash -r\b` misses any suffixed spelling. Drop the trailing
boundary on all three.
Mutation-verified: injecting `trap - ERR EXIT INT TERM`, `trap -- EXIT`
or `unset -fv sha256sum` into a PAT-bearing step now turns the suite
red; each passed before.
* fix(autofix): two -e-fatal paths, an escape-order dedupe hole, and a rewording duplicate (review round 10)
Six Criticals; four were defects this PR introduced.
- R10-19 + R10-22 (Critical): the PAT steps run under 'bash -eo
pipefail' (defaults.run.shell: bash). Measured: 'rm -f' on a planted
DIRECTORY at the predictable log path exits 1 and kills the step, ': >'
onto one likewise, and $(<missing) is fatal in a way NEITHER '|| true'
NOR 'if !' rescues. Creation is now 'rm -rf' + '(set -C; : >)' with a
warn-and-skip, and the read-back tests -f/-r first while staying
fork-free. Probed all four planted shapes (fresh/file/symlink/dir):
the step survives each.
- R10-5 (Critical): the <!-- neutralization ran in a sed AFTER the jq
corpus comparison, so an rv/ic line carrying <!-- compared its RAW
rendering against the ESCAPED stored form — never matched, republished
every round. Escaping moved inside jq, before the compare.
- R10-17 (Critical): rv/ic identity was the exact rendered line, so any
reworded re-emission (routine: the repair flow re-runs the agent)
published a permanent duplicate. Identity is now a normalized digest —
case-folded, punctuation-collapsed, trimmed, capped — which absorbs
phrasing churn while keeping distinct findings apart. The tension with
R9-2 is real and resolved deliberately toward a visible duplicate over
a silent loss.
- R10-1 (Critical): the sweep tripwire is a spelling denylist over an
unbounded space. Reframed as what it is — a drift alarm, not the
boundary (the boundary is the env -i child, pinned separately) — and
aimed at the ENUMERATION PRIMITIVES a sweep needs (compgen -e,
declare -x, env pipes, export -n) instead of more name vocabulary.
- R10-6 (Critical): the jq stub hardcoded /usr/bin/jq, which does not
exist on macOS; it now resolves through the original PATH.
- R10-13: the child's log comes from agent-writable RUNNER_TEMP, so ::
is neutralized on re-emission via parameter expansion (no fork).
- R9-5 leftover: the third truncation dump now names the clipped size.
Mutation-verified 5/5 and behaviour-probed 5/5 (escape-before-compare,
reworded re-emission, distinct sibling, R9-2 no-regression, planted-path
shapes).
* fix(autofix): make the deferral identity lossless; keep the unmerged set on disk (review round 11)
Two Criticals, both defects this PR introduced, plus the eleven items
carried from round 10.
- Identity key (Critical): the normalized key stripped every non-[a-z0-9]
byte and capped at 160 chars, so CJK siblings collapsed to one key (this
repo is bilingual) and a long path pushed the reason out of the identity
entirely — silent loss, the exact outcome the feature exists to prevent
and the opposite of what its own comment claimed. The key now normalizes
case and PUNCTUATION only, keeping every letter of every script and no
cap, at both the build and corpus sites. Rewording tolerance is
unchanged. Probed: 2 CJK siblings -> 2 of 2; 2 siblings on a 200-char
path -> 2 of 2; reworded duplicate -> 1 of 1.
- Repair merge failure (Critical): the branch deleted
deferred-findings.json before 'Show run artifacts' and the artifact
upload ran, so its own pointer at the artifact dump was false past the
4000-byte clip. It now renames the set to deferred-findings.unmerged.json
(kept in WORKDIR, added to the dump list) on the failure path and deletes
only on the merge-success path. Probed: 6245 bytes preserved where the
dump clipped at 4000.
- R10-12: neither side is known to be the corrupt one (jq -s fails if
either input is unparseable), so both merge-failure warnings say that
instead of blaming this round.
- R10-18: a second identity anchor — the derived title — so an edited body
that loses the marker no longer orphans the issue into a duplicate; the
marker still wins when both are present, and a same-titled PR is still
never adopted.
- R10-3: the carry branch is unreachable in today's topology (WORKDIR is
wiped at run start, one repair step); kept as defensive with that stated.
- R10-10: the builtins-only discipline is scoped to the child-output
INSPECTION, which is what it always meant.
- Pins: delimited-token allow-list incl. UPSERT_LOG's value (R10-7), every
respelling of executing the staged path (R10-8), the tripwire extended to
the issue-autofix failure steps (R10-11), launch-line anchoring in both
steps (R10-15), flagless rm counted (R10-16), and behavioural cases for
the 200/500 caps (R10-4), CJK and long-path siblings, and the title
anchor.
Mutation-verified 7/7.
* refactor(autofix): remove the agent-writable paths the upsert depended on (review round 12)
Rounds 9-12 each closed one hole in a design that read the staged script
from an agent-writable path and buffered the child's output through
another. Round 12 found four more of the same class (TOCTOU on the log
reopen, a plantable FIFO and an unbounded read on each path). Rather than
patch a fifth time, remove both paths.
- The script travels as CONTENT: the stage step captures it from the
trusted checkout into a step output (random heredoc delimiter), and the
clean child runs `bash -c "$UPSERT_SRC"`. With no agent-writable copy
there is nothing to verify — the digest gate, its check-then-use
window, the staged cp and the FIFO/huge-file read all disappear.
- The child's messages travel on fd 3, which the parent captures, while
fd 1/2 are discarded. Every loader side channel writes there, so the
noise still cannot reach the parsed output — and there is no log file
to plant, race, bound, or clean up. Probed with LD_SHOW_AUXV and
LD_TRACE planted: clean output, sentinel behaviour unchanged.
- RC-1: resolved-comments.txt went to jq as one argv element, the exact
MAX_ARG_STRLEN failure the neighbouring comment describes and that
`known` already avoided. Both corpora use --rawfile now. Measured on a
348 KB corpus: the old form dies with "Argument list too long", the new
one publishes normally.
Net -113 lines, and the pins follow: no-path invariants replace the
digest/log battery. Mutation-verified 6/6.
* fix(autofix): reject multi-document deferral files; survive a base without the script (review round 13)
- Multi-document JSON (Critical): `jq -e` without -s evaluates each
document in turn and its exit status reflects only the LAST, so
`[valid]\n[]` exited 0 silently (findings lost, no warning) and
`[bad-id]\n[valid]` passed the shape gate outright. A single_doc gate
now runs first, with the asymmetry the earlier rounds settled on: a bad
OWN file is a total abort, a bad CARRY costs only the carry.
- R13-7: the stage step reads the script from the TRUSTED BASE, where it
does not exist until this PR merges — under -e that killed every
pre-merge pull_request-triggered round (true of the old cp too, so this
has been latent since the script was added). It now tolerates the
absence and lets the consumers' own empty-content guard skip the round.
- R13-2: the carry union requires both inputs to BE arrays; `add` on two
non-arrays yields whatever they add to.
- RA1R4-B: the resolved-corpus test is -f/-r, so a directory or FIFO at
that path is treated as unusable rather than present.
- R13-1: nine rationale comments still described the staged copy and the
digest gate that round 12 removed.
Probed: both multi-document shapes are rejected loudly with zero writes;
a bad carry still leaves this round publishing 1 of 1.
* docs(autofix): retire the last stale rationale comments (R13-1)
Six of the nine locations were in the test file: comments still describing
the digest gate, the staged copy and the read-once invariant that round 12
removed. Same honesty issue as their three workflow siblings.
* fix(autofix): strip BSD wc padding; let wrapper warnings stay annotations (review round 14)
- BSD wc (Critical): `wc -l` pads its count with leading spaces on
macOS, and TOTAL_NEW is interpolated into the cap warning and the
success line — the sibling `wc -c` already stripped it, this one did
not. Tested with a padding `wc` stub, since GNU wc never pads and the
regression is invisible on Linux CI otherwise.
- The re-emit loop demoted the feature's own failure signal: every `::`
became `;;`, including the wrapper's trusted messages. Wrapper-authored
lines now carry a marker and are emitted VERBATIM (so they render as
annotations again); the script's output, which interpolates agent
content, stays neutralized.
- \b on `declare -x`/`export -n` had the same word-boundary hole already
fixed for `trap -`/`unset -f`.
- Pins: the heredoc CLOSING delimiter, the merge-failure quarantine
rename, and an allow-list comparison that is a sorted multiset rather
than a Set — a symmetric same-name addition is exactly what that check
exists to catch and a Set hid it.
Mutation-verified 5/5.
* fix(autofix): un-truncate the sibling identity; align the carry precedence (review round 15)
Zero Criticals this round; five behavioural findings among the pins.
- The rv/ic intra-batch identity was derived from the RENDERED line, i.e.
after the 500-char reason cap, so two siblings differing only past the
cap collided and one vanished silently — the same silent-loss class as
the CJK and long-path entrances. It now comes from the uncapped
path+reason; the corpus check still compares rendered forms (that is
all the issue stores), so cross-round the cap can cost a duplicate,
never a loss.
- The repair carry union put the OLDER set first, inverting the
newer-wins precedence the script documents for its own union.
- The resolved-id parser dropped any line with stray surrounding
whitespace, so a padded `rc:<id>` no longer suppressed its finding.
- The clean child's catch-all warning lacked the trusted marker added
last round, so the feature's most common failure message was still
demoted out of annotation form.
- The truncation notice pointed at the artifact dump even when the
dumped file is a merge temp outside WORKDIR, which is never uploaded.
Pins: the stage step's `id: 'stage'` (the link whose break empties every
UPSERT_SRC), the empty-content skip branch, the capture's `|| true`, and
the trusted marker on every warning inside the child.
Mutation-verified 6/6.
|
||
|
|
337da2143c
|
fix(ci): stop dropping agent settings in resolve and follow-up workflows (#9252)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* fix(ci): stop dropping agent settings in resolve and follow-up workflows Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(ci): pin remaining agent-settings guard gaps from review Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
f9bc8cb250
|
fix(autofix): re-anchor growth divergence on measurement time and external head moves (#9192)
* fix(autofix): re-anchor growth divergence on measurement time and external head moves Tightens the growth-divergence comparability window (PR #9104 follow-up, tracked as #9114): - measured_at (R2-6): the growth-now marker now carries the prepare-time measurement instant, and the divergence read filters on it instead of the comment's created_at. The report posts the marker only after the agent's ~120-minute run, so a round in flight when a concurrent base update landed would otherwise pass a created_at filter while carrying sums measured against the old base. - external head move (R2-8, subsumes R6-3): prior sums are measured against origin/main, so any commit an external actor (author push) or a stale-base merge added since the bot last evaluated the branch inflates this round's sum relative to them. BASE_UPD_AT only tracks the bot's own update-branch merge; the new GROWTH_NOW_CUTOFF also re-anchors (drops all prior sums) whenever the checked-out head is not the bot's last judged head (LIVE_RED_HEAD), covering author pushes and base updates alike. The reader now dedups/orders per run by measured= (a re-run's fresh measurement wins). Contract tests cover the measured-based cutoff, the external-head-move re-anchor (both branches), and the writer→reader round-trip with the new field. 172/172. R6-6 (markers don't store the effective budget, so a mid-window budget raise counts old rounds against the new regime — fail-safe, one round early) stays tracked in #9114. * fix(autofix): drop the head-move re-anchor, keep the measurement-time filter Review found the head-move half of this change broken in three ways (all probe-verified), so it is withdrawn and returned to #9114 rather than patched under review: - R1-1 (regression): `autofix-redcheck` records the head the agent was GIVEN, frozen before its push — so after any pushing round the next round's head differs and the cutoff was set to now, dropping every prior sum. In the push regime OVER_ROUNDS_PRIOR could never reach the threshold and the #9104 handoff would never fire at all. - R1-2: the cut was stateless — the round after a correct re-anchor fell back to an empty cutoff and re-admitted every pre-move sum. - R1-3: with no redcheck marker (a crash round) the `-n` guard skipped re-anchoring across a genuine external move. A correct version needs both a bot-authored-move test and a PERSISTED cut; that is its own change. What remains is the measurement-time filter (R2-6), which stands on its own: the marker carries the prepare-time instant and the divergence read filters/orders on it instead of the comment's post-agent created_at. Also from this review: - R1-4: `measured=` is OPTIONAL in the scan, falling back to the comment's created_at, so deploying does not blank an in-flight window's census. - R1-9: the per-run collapse now runs BEFORE the over/window/cutoff filters — a re-run whose fresh attempt came back under budget was still represented by its stale over=true attempt. - R1-7: comments corrected — run= is the DEDUP identity, measured= the ORDER key (four sites). - R1-8: recorded as a known residual next to the sibling growth-base reader, which still filters on created_at; tracked in #9114. - R1-5/R1-6: fixtures decouple created_at from measured=, cover a legacy marker (with and without the cutoff), and pin the measured_at source line in prepare. * fix(autofix): keep the failure-path growth marker scannable when prepare never ran * fix(autofix): prefer explicit measured= over created_at fallback in the per-run growth collapse * test(autofix): pin the explicit-measured preference in the per-run collapse |
||
|
|
e93da9e387
|
feat(autofix): escalate stopped takeover PRs and age out unanswered pauses (#8960)
* feat(autofix): escalate stopped takeover PRs and age out unanswered pauses
Takeover PRs that hit the round cap (or a circuit breaker) went silent:
no label, no dashboard entry, no escalation — five PRs had been paused
for days. The fleet shepherd only tracked bot-authored PRs, so the whole
35-PR human takeover pool was invisible.
The autofix scan now applies an autofix/needs-human label whenever a PR
reaches its cap (the write rides every cap detection, so already-paused
PRs backfill on the regular scan rotation), and removes it wherever
management resumes or a human releases the PR. The fleet shepherd
enumerates the takeover pool onto its dashboard (state, stop reason,
pause age, plus an awaiting-human section for released PRs) and gains a
single bounded lever: a takeover whose pause went unanswered for
AUTO_RELEASE_DAYS days gets its takeover label removed with a bilingual
summary, keeping the needs-human label as the filterable TODO. Resume
evidence newer than the pause notice — bot markers, trusted re-arm
commands, fresh labeled events — vetoes the release; every read fails
closed and a per-tick cap bounds blast radius.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): harden the takeover auto-release against review round 1
Addresses the PR review's two Criticals and eleven Suggestions:
- Command-comment resume evidence now counts only while FRESH (2h grace)
and UNSUPERSEDED by a refusal ack (fork-refused/base-refused/
skip-blocked) — an ignored command expires instead of vetoing the
release forever, and no permission logic is mirrored from the route.
- The release lever's population comes from the needs-human enumeration
(needs-human ∩ takeover), never the display window; both enumerations
cap at 100 with saturation warnings, and a failed enumeration degrades
to an error row so the dashboard write (and its liveness watermark)
always runs.
- The auto-release summary posts before the label DELETE, dedup'd by its
own marker — neither half can strand the other on a transient failure.
- Awaiting-human rows use neutral wording (capped bot PRs land there too)
and a shepherd-side heal clears stale needs-human labels left by manual
UI releases on fork PRs (human unlabeled event, budgeted, skip-vetoed).
- Fail-closed deferrals now still render a dashboard row (the row append
moved outside the evaluation arms); tick summary and dashboard header
report the same counters; days_since() replaces pasted epoch math.
- Tests: command-evidence gate replays (fresh/refused/expired/acked),
refusal-variant and command-string cross-file pins, DELETE-target and
fallback-assignment pins, heal jq replays, unified-row-render pin.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 2 — cycle-scoped heal, retryable summary
- The stale-label heal now only counts a human unlabel NEWER than the
latest label-apply, so an unlabel from an earlier takeover cycle can no
longer heal the current cycle's needs-human after an auto-release (R2-1).
- The summary dedup marker is scoped to the current pause cycle (markers
older than the latest cap notice are ignored), so a re-armed and
re-capped PR still gets its second release summary (R2-4).
- The two DELETE levers no longer redirect act()'s stdout, keeping the
DRY-RUN preview and failure warning visible (R2-5).
- AUTO_RELEASE_DAYS is base-10 normalized after the numeric guard, so a
zero-padded repo variable can't silently kill the lever (R2-6).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 2 — cycle-scoped markers and mutation-tested pins
- Heal is cycle-correlated: only a human unlabel NEWER than the latest
label-apply counts (an earlier cycle's unlabel can't heal this cycle).
- The release summary dedup marker is scoped to the current pause cycle,
so a re-armed and re-capped PR still gets its second summary.
- act() stdout is no longer redirected on the two DELETE levers (DRY-RUN
preview and failure warning stay visible).
- AUTO_RELEASE_DAYS is base-10 normalized so a zero-padded repo variable
cannot silently kill the lever.
- Doc/workflow-header text corrected to the implemented order (summary
first, marker-dedup'd) and to the idle-backoff backfill timing.
- Mutation-tested test pins for every gap the reviewer probed: days_since
replay, NH_PREFIX interpolation + truth map, loop-1 deferral, full
cross-file marker/refusal-set equality, label-constant cross-pin,
EVENT_TS merge + promotion ordering, CLEANUPS increment, unclassified
headline classification, filter byte-identity, exit-spelling ban,
@uri encoding, sort/field-list attribution, paginate shapes, scope
--arg bindings, LIVE_LABELS_JSON wiring, positional append pin,
label-create idempotence + POST guard, and per-branch removal
attribution in the toggle replay.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 3 — release-ack label gate and shared classifiers
- R3-1 (Critical): the every-scan cap-branch label POST is now suppressed
when a release ack (takeover-ack released) is newer than the last re-arm,
so a released bot PR is not re-labeled each scan (which would fight every
release-side removal and ping-pong with the shepherd cleanup). A re-arm
advances the window past the release ack, re-enabling the label.
- R3-7: the /retry re-arm's needs-human removal now honors autofix/skip,
mirroring the takeover-command guard — a frozen PR keeps its only
filterable escalation state.
- R3-2 (Critical): the takeover-enum error row no longer claims 'no release
evaluation ran' — the lever is fed by the needs-human enumeration.
- R3-8: the conflict-dispatch lever refuses a paused (needs-human) PR
instead of spending a dispatch slot the scan would refuse.
- R1-10: extracted pending_checks()/failed_test_url() helpers so both
dashboard loops share one CI-status classifier (the round-1 reply was
wrong that the restructure removed this duplication — it did not).
- Hardened the mutation-tested pins: exact terminal-headline count (5),
full rearm DELETE line + single-API-write, AUTO_RELEASE_DAYS guard order,
runRearm env/stub/assertion for the /retry DELETE + skip guard, and the
scope-guard comparison operator.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 4 — label-lifecycle hardening
- R4-C1: the conflict-dispatch lever reads needs-human from the LIVE label
payload (after live_skip), not the tick-start snapshot, so a label applied
after enumeration is still honored.
- R4-C2: a re-armed PR that still carries needs-human (a resume-side removal
failed) now gets a bounded, skip-vetoed cleanup retry instead of staying
pinned in the paused population forever.
- R4-C3: the per-tick release budget is consumed before the first external
write — a DELETE outage can no longer mutate many PRs while RELEASES=0.
- R4-C4: dashboard row routing follows post-action label state — a released
PR moves to Awaiting human, a healed one drops off entirely.
- R4-C5: the AUTO_RELEASE_DAYS guard also rejects over-long digit strings
before any arithmetic (Bash-int overflow would wrap negative and pass -ge).
- R4-32: takeover-command stop only removes needs-human when the takeover
release actually landed (REMOVED_OK; 404 counts) — a failed release no
longer strands the escalation label while latching RELEASE_ACKED.
- R4-2: the /retry skip guard fails closed — an unreadable label state keeps
the label (mirrors takeover-ack's exit-1 convention).
- R4-3: the takeover-ack released arm and the stop branch both honor
autofix/skip when removing needs-human.
- R4-S1: producer headlines must be explicitly classified terminal or
transient — an unclassified headline now fails the cross-file test.
- Pins updated/added for every behavior above.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 4 — robust release detection and marker-true gates
- R4-1/R4-5: release detection now uses the takeover unlabeled EVENT
(recorded on every removal path, unlike the tolerated-lost ack comment),
and the suppression only applies to human-authored PRs — a bot PR
released from takeover returns to standard management and keeps the cap
notice + escalation label.
- R4-6: the conflict-dispatch lever requires marker truth (conflict_paused)
— an armed PR with a stale needs-human label is dispatched normally.
- R4-C1: the pause check reads needs-human from the live label payload.
- R4-C2: re-armed PRs with a stale label get a bounded cleanup retry.
- R4-C3: the release budget is consumed before the first external write.
- R4-C4: dashboard rows route on post-action label state.
- R4-C5: AUTO_RELEASE_DAYS rejects over-long digit strings before arithmetic.
- R4-32/R4-2/R4-3: stop/ack/retry removal paths gate on REMOVED_OK and skip.
- R4-9/R4-10/R4-13: membership check, STATE escaping, HM_OK-branched error row.
- R4-14: command evidence requires a write/maintain/admin commenter.
- R4-11/R4-15/R4-24: behavioral replays for the classifiers, the release
jq, and the gate nesting.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): keep the label-DELETE idiom byte-identical across workflows
R4-32's REMOVED_OK tracking reworked the takeover-command stop branch's
404-tolerance block, breaking the pr-self-report-label ↔ qwen-autofix
contract test that pins the two workflows' label-DELETE idiom
byte-identical. Keep the canonical idiom and derive REMOVED_OK from
REMOVE_ERR's content afterward (empty = landed, 404 = already off,
anything else = release did not land) — same behavior, contract intact.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(autofix): close review round 4b — mutation-tested harness hardening
- R4-4: re-bound the conflict-lever regex spans and anchor on
conflict_paused so the pin can't resolve live_skip against the sync
lever's call site.
- R4-16: runAck records gh calls and asserts per-branch needs-human DELETE
counts (engaged/released=1; base-refused/skip=0).
- R4-17: pin the first-pickup scan DELETE inside the engage-ack success
branch.
- R4-18/R4-19: ordering pins — takeover POST before needs-human DELETE
(engage), marker comment before cleanup DELETE (/retry).
- R4-20: deleteFail stub branch replays non-404 (warns, status 0) and 404
(silent) DELETE outcomes.
- R4-21: identity-failure paths assert no DELETE ran.
- R4-22: runRearm stub serves labels only when --json labels is requested.
- R4-23: full api-write census pinned (exactly api user + one DELETE).
- R4-25: skip fixture uses the production multi-label shape.
- R4-28: loop-2 fetch pins include the jq -s 'add // []' merge program.
- R4-29: cmdGate scenario where a refusal is OLDER than the fresh command.
- R4-30: takeoverEnum asserts its own sort:updated-asc qualifier.
- R4-31: multi-entry fixtures pin the max/last/length aggregation operators
on CMD_TS, EVENT_TS, REASON, SUMMARY_POSTED, and the heal lever's
LATEST_LABEL_TS/UNLABEL_ACTOR programs.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 5 — trust boundaries and evidence freshness
- R4-5 residual: PR_META now fetches author so IS_BOT_AUTHOR actually
resolves (the exemption was dead on arrival), with a behavioral replay.
- R5-1: REMOVED_OK derives from the captured stream ('HTTP ' non-404 = not
landed) instead of output emptiness — GitHub returns a body on success.
- R5-2: /retry only drops needs-human when management actually resumes
(takeover label present or bot-authored) — an auto-released human PR
keeps its escalation label.
- R5-3: conflict_paused requires a real cap notice AND a newer resume
marker — label-present/notice-absent now fails closed toward paused.
- R5-4: a failed permission read defers the release (PERM_READ_FAILED),
never counts as no-permission — at both evaluation points.
- R5-5: compute_resume_ts scans in-grace commands newest-first and
permission-checks each (≤2 reads), so a stranger's echo can't shadow a
maintainer's command.
- R5-6: the release branch re-fetches evidence and recomputes resume state
immediately before the first write.
- R5-8: the heal re-checks the takeover label from the live payload before
clearing needs-human.
- R5-9: same-second ties resolve toward resume/release suppression in both
files (RESUME>=TERM; RELEASE_ACKED >= window).
- R5-10: the heal anchors to the current pause boundary (latest needs-human
apply event); an absent anchor skips the cleanup, fail closed.
- Tests: whole-function compute_resume_ts replay (permission/shadow/tie/
grace/refusal cases), heal anchor fixtures, toggle stub models the real
DELETE body, runRearm orphan case.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 6 — contract-safe gates and subshell flag fix
- B5: the bot-fleet enumeration failure now degrades to a loud error row
and falls through (FLEET_OK gate) instead of exiting before the
independently-fed takeover/needs-human processing and the dashboard's
liveness-watermark write.
- B12: the cap-branch LIVE_LABELS consent re-read fails closed on an
unreadable gh pr view (a collapse to '' ignored a concurrently added
skip for standard bot PRs).
- R6-1/R6-19: the takeover-release landed flag is keyed on the DELETE exit
status (LBL_DEL_FAILED set inside the pinned idiom's failure branch) —
never on output text, which lies in both directions. The
pr-self-report-label idiom evolves identically to keep the cross-workflow
contract green (and its own 'removed' log line no longer lies either).
- R5-4 residual: compute_resume_ts now returns via globals
(RESUME_OUT/PERM_READ_FAILED) and both call sites invoke it directly —
the previous subshell silently dropped PERM_READ_FAILED, leaving
the fail-closed defer branches dead.
- R6-3: command candidates are deduped by author before permission reads,
so a stranger posting N commands can't burn the 2-read budget and shadow
a maintainer's command.
- R6-4: an unreadable release history is reported as such, not as
'released'.
* fix(autofix): close review round 7 — lever starvation, re-arm anchoring, permission shadows
- R5-7: the release lever gets its OWN enumeration of the paused population
(takeover+needs-human, stale-first) instead of the long-lived needs-human
display window — released-awaiting PRs aging back into that window could
truncate exactly the fresh pauses that become release-eligible, starving
the lever and making the zombie state permanent and self-feeding.
- R6-3: the 2-read permission budget now sets PERM_READ_FAILED on exhaustion
(it was failing open), and the candidate walk sorts newest-first per author
(group_by+max_by+sort) instead of unique_by's alphabetical order, so two
read-only strangers can't shadow a maintainer's newer command.
- R7-1: the stale-label cleanup anchors on the current pause boundary (latest
needs-human apply) and is marker-confirmed only — not keyed on TERM_TS, and
never on command/label evidence — so a re-paused PR with a lost cycle-2
notice isn't read as re-armed on stale cycle-1 evidence.
- R7-7: the /takeover stop success echo is gated on REMOVED_OK — a failed
DELETE no longer logs 'removed'.
- R7-2: TAKEOVER_COMMAND/RETRY_COMMAND mirrored into the shepherd env and
passed via --arg, so the resume matcher can't drift from the route.
- Tests: conflict_paused + re-arm guard behavioral replays, mirrored-command
cross-file pin, engaged/released-with-skip ack matrix cells, LBL_DEL_FAILED
branching, gnuDateShim hoisted to module scope, R4-24 nesting indices.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* test(autofix): complete the R4-16 ack-matrix DELETE-count coverage
Add fork-refused and skip-blocked ack cases to the takeover-ack harness
— management never resumed on either, so zero needs-human DELETEs, each
asserted by total DELETE count (not just toContain).
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 8 — reachable re-arm cleanup, release race guards, pin census
* fix(autofix): close review round 9 — honest release-failed ack, cleanup attempt budgets, dashboard single-owner routing
- /takeover stop whose label DELETE failed no longer posts a
'Takeover released' ack: a release-failed variant names the retry
(R9-4), and the R7-7 echo pair gains symmetric log pins (R9-3)
- stale-label cleanups count ATTEMPTS like the release budget, so a
DELETE outage trips the cap instead of leaving it inert (R9-5)
- dashboard renders each both-label PR exactly once: loop 1 defers by
paused membership, loop 3 is the render of last resort (R9-1/R9-13)
- a 404 from the collaborators-permission endpoint classifies the
author read-only instead of renewably deferring the release (R9-10)
- cap-branch release evidence reuses the per-iteration events fetch
under a success flag (R9-18); release-clock comment corrected (R9-11)
- harness gates end-anchor the --json field list (R9-14/R9-15); the
escalation POST and the ack-body census gain count pins (R9-16);
the date shim answers only the +%s shape it emulates (R9-9)
* fix(autofix): close review round 10 Criticals — exact HTTP 404 release classification, isolated replay fixtures
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(autofix): close review round 11 Criticals — engaged stale-ack guard, exact HTTP 404 permission classification
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
|
||
|
|
90f754e73e
|
fix(ci): keep no-op review requests out of the PR review concurrency group (#9210)
* fix(ci): keep no-op review requests out of the PR review concurrency group Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(ci): pin precheck-pr bot login to the review constants * test(ci): share one bot-login extraction across review-workflow suites * fix(ci): route every review request to a per-run concurrency group A bot-directed review_requested run joined the shared PR group on the requested reviewer's identity, but whether it reviews anything is decided later by authorize on the requester's write permission. A requester without write produces a guaranteed all-skipped run that can still supersede a lifecycle run sitting PENDING behind a still-terminating review — the same lost-review race as #9091, through the bot-request door. Gate the shared group on the action alone so no review_requested run can supersede a pending lifecycle run; an authorized bot request still reviews immediately, at the cost of an occasional duplicate review of the same head. --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
5b125f0b89
|
fix(ci): minimize spam inline review comments (#9229) | ||
|
|
9b39280078
|
fix(ci): skip non-bot review_requested siblings before jobs spend compute (#9204)
Opening a same-repo PR that touches CODEOWNERS-covered paths auto-requests every owner individually, so one PR open emits one review_requested run per owner (five within the same second on #8830/#9142). Only the bot-requested run can reach review-pr; the human-requested siblings used to spend a review-config runner plus an authorize job (CI_BOT_PAT permission API) each before no-op exiting. Mirror the requested_reviewer predicate precheck-pr already applies to fork PRs into authorize.if and review-config.if so the siblings complete as instant all-skipped runs. Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com> |
||
|
|
e7a7ac1bfb
|
feat(autofix): deny-by-default footprint gate and positional window censuses (#9156)
* feat(autofix): deny-by-default footprint gate, positional window censuses, review-loop backlog Follow-up to #8981/#8996, closing the structural causes behind their review-round non-convergence: - Deny-by-default footprint: every file a round touches maps to an AREA (declared workspace, else top-level directory, else the root file itself); areas outside the PR's own footprint are surfaced in a gate-authored advisory, or rejected retryably once the repo variable QWEN_AUTOFIX_FOOTPRINT_ENFORCE is staged to 'reject'. The enumerated class gate keeps rejecting regardless — this inverts the default for everything it cannot enumerate (a denylist is not a boundary). - The three window censuses (PRIOR_TIMEOUTS, WIN_HEADS, PRIOR_HEADS) attribute comments positionally over their own scan-parsed eval markers instead of whole-body win= substrings: a neutralized marker quoted in a handoff excerpt, or any future marker embedding win=, can no longer double-attribute a comment (decoy fixture included; the census fixture's non-numeric round= placeholder is corrected). - BITE_ENFORCE's reply arm inherits the thread root's CHANGES_REQUESTED membership, not just its body tag. - Backlog tests: the bite restore-failure crash contract (verdict-less exit with the rejection document, driven by a ref-deleting runner), merge-base-anchored footprint compares under an advanced main (afterPr fixture hook), and the shrink+bite advisory append order. - SKILL: cap each round's implemented batch (~8 findings, Critical first, defer the rest via comment-replies) — nine review rounds of evidence that oversized fix batches breed fix-of-fix defects — and document the footprint gate. * fix(autofix): close the R1 footprint-gate findings - Advisory lifecycle: one reset at gate start, every writer appends — the footprint advisory no longer dies to the shrink section's rm or its truncating write. - Footprint membership is REF-ANCHORED: areas derive from the pre-round root manifest's workspaces globs (longest ancestor wins, nested workspaces correct), so a round cannot redefine its own boundary and the on-disk resolver is out of this path entirely; non-workspace paths under packages/ keep two segments so sibling projects stay distinct areas; emitted areas are newline-sanitized against phantom footprint grants. - The enforcement knob rides step-level env at both verify gates — $GITHUB_ENV writes from earlier steps cannot downgrade 'reject'. - TESTSIDE's critical() mirrors cr_attached (root and self), keeping enforcement and demotion on one comment set. - Census ownership is LAST-WINS over scan-parsed markers (a stray quoted-or-appended marker cannot double-attribute), the replay decoy is now genuinely discriminating (old whole-body → 0, new → 1), and the growth-gate comment stops citing retired whole-body matchers. Queued per the batch cap: per-line advisory bullets and the third sink charset, discriminating fixtures at the two remaining census sites, the reply-arm bite fixture, freight and merge-base footprint fixtures, and digest-pinning the staged resolver for its remaining consumers. * fix(autofix): close the R2 footprint-gate findings - list_areas reads and translates the ref's workspaces globs ONCE per invocation and matches ancestors in-bash (was ~21ms git+jq+sed per file×ancestor call), emits printf %q keys — line-safe AND injective, so distinct areas can never collapse into one comparison key — and both render arms print one bullet per area from those keys. - Producer failures are a STATE: a failed round- or PR-side diff (orphan history, transient git error) skips the footprint check loudly instead of shrinking one side into a verdict. - The workflow-level FOOTPRINT_ENFORCE env is gone (the step-level pins are the only consumers and outrank it — dead config removed); the two step wirings are count-pinned. - Fixtures: nested-workspace membership discriminates against the packages/ two-segment fallback (sibling nested workspaces stay distinct areas), and the advisory-lifecycle discriminator proves an earlier section's advisory survives the shrink section. Queued: consolidating the six eval-marker regex variants behind one grammar constant (touches six jq programs; its own change). |
||
|
|
4ee6a087e5
|
feat(autofix): judge review-feedback validity by content, not author (#8996)
* feat(autofix): judge review-feedback validity by content, not author
Wrong feedback drives wrong rounds regardless of who wrote it: maintainers
increasingly draft comments with models, so author identity carries no
correctness signal. The trust gate stays as the injection/authorization
boundary it always was, but the validity layer becomes source-blind and
execution-based, enforced by the verification gate rather than prose.
Three mechanisms:
- Bite check: a round changing both source and tests has its changed tests
re-run against the pre-round tree (origin/<branch> sources with the
round's test files overlaid). All green there means the claimed defect
never reproduced — the shape of a plausible-but-false finding implemented
as a fix — and the round is rejected, non-retryable, with the measurement
in LAST_REJECTION so the next round can decline or escalate the finding.
Fails open on every scope limit: single-workspace rounds only (gitignored
dist carries the round's build across the detach, the same confound that
A/B-exempts typecheck), runnable unit tests only, and any pre-round
failure counts as biting.
- Sensitive-area footprint: a round may not expand into CI/verification
machinery the PR itself never touched — .github/, .husky/, eslint/vitest/
tsconfig configs, and the scripts section of existing root or first-level
workspace manifests (the gate's own command surface). Judged by area
class so takeover on an infra PR keeps full freedom; round-added
workspace manifests are exempt. Rejected retryably (the repair pass can
revert).
- Test-deletion advisory: shrinking coverage is surfaced by a gate-authored
section in the round report (deleted files, net test lines), never by the
agent's own prose, so a maintainer reads the agent's justification next
to the machine measurement.
SKILL.md rewrites the address-review protocol to match: identical
verification for every author, probe evidence outranks any assertion,
refuted maintainer claims are escalated with the measurement instead of
silently obeyed or overridden, and severity tags alone no longer make an
item Required — the claim must be checkable and reproduced.
* fix(autofix): harden the validity gates per review round
- Scan round/PR diffs NUL-delimited with --no-renames: a rename out of a
sensitive area now classifies the vacated source path (moving a
workflow out of .github/ is a removal of verification machinery), and
specially named files are no longer core.quotePath-mangled past the
case patterns.
- Narrow the capability classes: .github/workflows|actions, .github/
scripts, and passive .github metadata are separate areas (an
issue-template PR no longer licenses workflow rewrites), and the
transitive executable surface — repo scripts/ (minus scripts/tests/)
and .npmrc/.nvmrc — joins the protected set.
- Gate the bite consequence on machine-read intent: rejection now
requires the round to RESOLVE a Critical-tagged or CHANGES_REQUESTED
finding (resolved-comments.txt matched against rc.json/rv.json);
every other src+test round gets a gate-authored advisory on all-green
instead — a behavior-preserving refactor pinning existing behavior is
no longer rejected.
- Drop the blanket *.md exclusion from bite source detection: skill
markdown is executable agent behavior, and the intent gating now keeps
doc-only rounds safe from rejection.
- Sanitize deleted-test filenames in the gate advisory through a safe
character set: a backtick in a legal git filename could close the code
span and forge gate-authored markdown.
- Replace per-path basename spawns with parameter expansion.
- Tests: rename-evasion, metadata-vs-workflow class split, repo-scripts
class with the scripts/tests carve-out, filename-forgery rendering,
enforce-vs-advisory bite consequences (Critical tag and CR review),
and tree-state-proving runners that flip on pre-round source with the
round's test overlaid (plus the round-leak negative control).
One reviewed finding is declined with evidence in the thread: existential
batch semantics for mixed Critical rounds (per-behavior probe binding
needs test-result parsing; documented as a known limit at the check).
* fix(autofix): close the round-2 validity-gate findings
Sensitive-area scan: read NUL records directly (no tr re-mangling — a
newline filename cannot mint phantom footprint grants); resolve declared
workspace manifests and workspace-root configs through the trusted
resolver (nested workspaces protected, src-tree scaffolds exempt); split
root vs workspace manifest classes; guard the root workspaces array; give
the loop's own workflow and gate script their own class; classify .qwen/
(skills are executable agent behavior); anchor footprint content compares
at the merge base; sanitize violation paths in the rejection document.
Bite check: tolerate rc:-prefixed and CRLF resolved-comment ids (the
handle format SKILL prescribes — enforcement never fired without this);
count replies resolved in Critical-rooted threads as defect claims; skip
non-vitest workspaces (a vacuous --if-present pass must never reject),
self-package-name imports (dist confound), and rounds with paths outside
the resolved workspace; include renamed tests and changed snapshots in
the overlay; drop nested fences from the rejection document; surface
test-only defect claims as an advisory; document the already-fixed
re-raise limit and steer it to a no-code round.
Tests: classifier probe over every arm, footprint cases for the new
classes, enforce-vs-advisory negatives, reply-root enforcement, and the
rc:/CRLF handle round-trip.
* fix(autofix): close the round-3 Critical findings on the validity gates
- Gate-consumed helper scripts (resolve-owning-packages, settings-schema
and contracts checks) join the autofix-loop class: an unrelated
.github/scripts footprint no longer licenses rewriting machinery the
gate executes.
- Skip round-scan files whose content equals current origin/main: a
round that merges main (the flow SKILL prescribes on conflicts) made
ROUND_RANGE degenerate and attributed all incoming main churn to the
round, false-rejecting ordinary base updates.
- Round-added workspace-root configs are the round's own surface (same
cat-file exemption manifests have); deleted workspace manifests are
classified from pre-round existence instead of the on-disk resolver
that can no longer see them.
- The bite vitest guard reads the PRE-ROUND manifest — the tree whose
test script the detached runner actually executes.
* fix(autofix): close R4 validity-gate findings — gate-consumed surfaces join the taxonomy
- Supply-chain surfaces classify: lockfiles/shrinkwraps (root and nested)
and patches/ (patch-package runs on every install) as supply-chain;
.gitattributes (root and nested) as measurement-config — a -diff rule
could blind numstat-based advisories.
- manifest_scripts_changed inspects resolution fields too: workspace
manifests compare {scripts, exports, main, types}; the root manifest
adds exports alongside workspaces.
- resolve-sandbox-image.mjs joins the autofix-loop class (it establishes
the loop's isolation boundary).
- The noop path emits verified_head, making the prescribed no-code
re-verification round mechanically able to resolve threads.
- The bite transcript is cleaned at gate start like its sibling logs;
the advisory's test definition aligns with the growth brake's six
globs (__tests__/, test-utils/ included).
R4-3 (post-round on-disk workspace resolution racing a same-round
workspaces negation) is declined in-thread: it requires the PR footprint
to already license manifest-scripts-root, which is the accountability
boundary working as designed; pre-round-tree resolution is queued with
the census follow-up. R4-5 (advisory in failure paths) queued likewise.
* fix(autofix): deflake the bite harness and align the test taxonomy
- Isolate fixture git from ambient global/system config (the sibling A/B
fixture's GIT_CONFIG_GLOBAL=/dev/null pattern) and fail loudly on spawn
errors with the exit status in the assertion message — the advisory
sub-case intermittently died spawn-level under load with empty streams
and no diagnostic (reproduced 1/6 locally, once on CI).
- BITE_SRC excludes __tests__/ like the gate's own TEST_PATHSPEC.
- SKILL's boundary enumeration names the supply-chain and
measurement-config classes and the full protected manifest fields.
* fix(autofix): close R6 validity-gate findings
- Test-side defect claims take the advisory arm: when every resolved
Critical thread sits on a test file (rc.json .path), the fixed test
legitimately passes pre-round — enforcement grade 'advisory', never a
rejection; the test-only advisory also no longer requires a matching
*.test.* glob (snapshot-/helper-only resolutions surface too).
- Classifier arms: newline-bearing paths fail CLOSED as their own class;
qwen-pr-safety-precheck.yml + pr-safety-precheck.mjs join autofix-loop;
nested .npmrc/.nvmrc; eslint.legacy-filenames.mjs (imported by the lint
leg's config); root manifest filter carries main/types.
- The self-import dist-confound guard matches the package name delimited
(quote or subpath), so @qwen-code/qwen-code no longer swallows its
-core sibling's imports.
- Test isolation extends to the footprint and advisory spawns (R5's
rationale applied everywhere), spawn errors fail loudly there too, the
classifier probe pins the supply-chain/measurement-config arms, the
coverageOnly fixture asserts the advisory text, and the neutralization
ledger header matches its count.
- The resolve-threads design doc records the widened no-op
verified_head rule and its safety argument.
Deferred to the backlog per the convergence note: the bite-side restore
crash-contract test (shared-fixture work), origin/main-advanced footprint
fixtures, and advisory append-order pins.
* fix(autofix): close R7 validity-gate findings
- The resolve/reply pass is a shared function serving BOTH the pushed and
no-op outcomes: the no-code re-verification escape can now actually
resolve threads, and no-op declines finally post their in-thread
replies (a pre-existing silence gap). The design doc states the shared
path, its guards, and the named first-round residual.
- TESTSIDE demotion votes only over resolved CRITICAL threads (a source
Suggestion resolved alongside no longer breaks it; a source Critical
alongside keeps full enforcement) — three fixtures pin the matrix.
- The shrinkage advisory measures with --no-renames (a rename out of
runner discovery is a shrink) and NUL-safe deleted names.
- Bite inputs pass through the merge-freight filter the class scan
already applies, and BITE_SRC collects NUL-safe.
- Demoted rounds get their own advisory text (all-green is their
expected shape, not a failed reproduction).
- The manifest block comment matches the resolver-backed code; the
footprint and advisory test spawns get the isolation and loud
spawn-error handling previously claimed — the R6 reply overstated
that fix and this commit is the correction.
* fix(autofix): close the review-body re-checks on the validity gates
- Deleted-manifest classification honors the fixture exemption from the
PRE-ROUND root manifest's workspaces globs (was_workspace_dir) — a
deleted src-tree fixture manifest is no longer false-rejected, while a
deleted declared workspace still classifies; the PR-footprint scan
gets the same treatment anchored at the merge base, so a PR-deleted
workspace keeps licensing later rounds.
- A config added into a PRE-EXISTING workspace is machinery (the gate's
legs execute it); only a config born with its round-added workspace
keeps the exemption.
- The shrinkage advisory applies the merge-freight skip per file (NUL
numstat records), so a base-merging round is not charged main-side
test churn in trusted-voice text.
- The bite rejection document renders filenames through the safe
charset and collapses backtick runs in the runner tail below the
outer fence length.
- AGENTS.md/CLAUDE.md classify as agent-policy; the root-manifest
comparator covers lint-staged and config (sandboxImageUri) too.
Still standing by recorded design, acknowledged in the review body:
R1-9 (already-fixed re-raise), R1-27 (existential batch semantics),
R4-5 (post-round resolver vs same-round workspaces negation).
* fix(autofix): close the round-9 validity-gate re-checks
- TESTSIDE's critical() carries the CHANGES_REQUESTED review-state arm
and receives rv.json, mirroring BITE_ENFORCE — a CR-enforced test-side
claim demotes to the advisory arm, and a CR-enforced source claim can
no longer collapse into it (R8-1, both directions).
- was_workspace_dir matches workspaces globs PATH-AWARE ('*' stops at
'/', '**' spans, '?' single, '!' entries skipped conservatively): a
nested src-tree fixture manifest deletion no longer false-rejects
while a declared workspace deletion still classifies (R9-1); both
pinned by fixtures.
- The PR-footprint manifest arm answers aliveness and membership from
refs (origin/<branch> / merge base), never the round's on-disk tree —
a PR-added workspace a round later deletes keeps its footprint class
instead of walling the deletion (R9-3).
---------
Co-authored-by: verify <verify@local>
|
||
|
|
22bacfe249
|
feat(autofix): escalate a non-converging diff to a maintainer handoff (#9104)
#8981's growth brake trims non-Critical feedback once a window's diff grows past budget, but when the growth is Critical-driven (a complex feature whose every fix opens the next fail-open gap the reviewer then flags — e.g. PR #8777, 8 rounds, 13k additions) Critical-only cannot help: the Criticals ARE the growth, so the diff keeps climbing and the agent keeps patching. Two additions on the autofix side: - Feed the growth trajectory to the agent. feedback.md now opens with a "Diff growth this window" section (net src/test vs budget + how many prior rounds were over budget) whenever growth is measured, telling the agent to prefer minimal/subtractive fixes and to read a rising trajectory as a signal to escalate for a split, not add another guard. - Detect divergence and hand off. A new per-round autofix-growth-now marker records each round's growth + over-budget flag; prepare reads the window's history and, once the brake has been over budget for >= GROWTH_DIVERGENCE_ROUNDS prior rounds (default 2, tunable) AND the diff has not shrunk from its worst, injects a "Needs a maintainer's decision — this PR is not converging" block. It is framed as a defer-to-human item, so the address run stops BLOCKED with a handoff (split / accept core + track the tail / redesign) instead of patching again. A diff that is over budget but shrinking, or a one-off overshoot, stays in ordinary Critical-only. SKILL.md documents both blocks. Contract tests pin the knob, run the extracted divergence detector against fixture history (climbing → diverged, shrinking → not, sub-threshold → not, wrong-window → not), and assert the growth-now marker is written on both report paths. |
||
|
|
8189c284ae
|
fix(ci): use repository token for spam minimization (#9140) | ||
|
|
6ebc79f61c
|
ci(vscode): gate sync publish on RELEASE_VSCODE_SYNC_PUBLISH variable (#9132)
The VSCode IDE Companion workflow releases in sync with every stable CLI release via the release: published trigger. Add a repository-variable switch so the sync path can be paused for a period without touching the workflow: setting RELEASE_VSCODE_SYNC_PUBLISH=false skips the release-event path (prepare and, transitively, build/publish), while manual workflow_dispatch releases keep working. Unset or any other value keeps today's behavior. Co-authored-by: yiliang114 <yiliang114@users.noreply.github.com> |
||
|
|
c9cb53398d
|
refactor(cli): Generalize the Conversations runtime foundation (#8890)
* docs: Design standalone daemon sessions Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: Align standalone session implementation stages Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * refactor(cli): Generalize the Conversations runtime foundation Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: Clarify Conversations runtime lazy startup Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Cover Conversations lifecycle guards Clarify standalone transaction recovery outcomes and remove the unused Conversations-only Live Host workflow trigger. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#8890) Harden owned runtime publication and complete the standalone transaction safety contract. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#8890) Keep the dedicated Live Host workflow aligned with the generalized Conversations runtime path. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Keep owned runtime validation unpublished Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Address Conversations review coverage Clarify standalone lifecycle failure and compatibility contracts, and pin the Conversations runtime publication and ownership invariants identified in review. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#8890) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#8890) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * codex: address PR review feedback (#8890) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
6e21f72f57
|
fix(autofix): hold autofix rounds while review-pr is in flight (#8899)
* fix(autofix): hold rounds while review-pr is in flight (#8888) * fix(ci): harden autofix review-in-flight gate * fix(ci): ack deferred review fallback runs * fix(ci): hold only cancelable automatic reviews in the gate (R2-1) * fix(ci): bound review run fallback * fix(ci): gate infra reruns behind review liveness |