mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
main
1281
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f8256e8222 |
fix(release): derive the next patch when the preview baseline lags latest (#16470)
* fix(release): derive the next patch when the preview baseline lags latest (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): derive the stable bump from the rollback-aware latest (#16466) Co-authored-by: Qwen-Coder <[email protected]> * refactor(release): share the published-latest bump helper (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse cross-line stable versions from older refs (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse stable releases from refs behind latest (#16466) Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse cross-line and stale stable derivations (#16466) Co-authored-by: Qwen-Coder <[email protected]> * test(release): pin the cross-line guard's strict boundary (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse a derived base above the checked-out ref (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): keep same-line next-patch promotions allowed (#16466) Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
708ba347d3 |
feat(ci): add disposable external PR runners (#16513)
* feat(ci): add disposable external PR runners * test(ci): align platform lanes with disposable runner routing --------- Co-authored-by: yiliang114 <[email protected]> |
||
|
|
cf29f90062 |
fix(ci): route the review/resolve permission gate to the ECS pool (#16482)
* fix(ci): route the review/resolve permission gate to the ECS pool The authorize job gates every @qwen-code /review and /resolve command, but its ECS routing required github.event.pull_request, which issue_comment and workflow_dispatch payloads do not carry. Every command therefore fell through to ubuntu-latest, and the gate sat in the hosted queue for hours (PR #15286: 3h10m for a 4-second job) while the ECS pool was idle. Route issue_comment and workflow_dispatch to the persistent ECS pool, matching qwen-autofix.yml's route/review-scan pattern for PAT-bearing API-only steps. authorize runs no agent, checks out nothing, and processes no PR content, so this does not widen the fork attack surface. PR-payload events keep the same-repo fork guard; the hosted fallback and ECS kill switch are unchanged. resolve-pr and publish-resolution stay on hosted by design (ephemerality); this removes the gate wait only. * fix(ci): harden the authorize ECS-routing pins and risk docs Address review feedback on the gate routing change: - Pin the checkout-free invariant the ECS routing depends on: a uses: step on authorize would execute contributor-authored files on the persistent pool with CI_BOT_PAT in the step env while the expression pin stayed green (R1-1). Scoped to authorize; mutation-witnessed (adding actions/checkout reds it). - Record the trigger-set widening in the design-doc Risks (both languages): issue_comment makes the gate fireable by any GitHub user on a fork PR, and the should_review authorization fact is now produced on the shared host and consumed downstream without re-deriving (R1-2). - Correct the precedent wording: route loads only the read-only github.token; review-scan is the job that loads CI_DEV_BOT_PAT on the pool. * fix(ci): harden the shared-pool authorize gate against gh config reroute Address the round-2 CHANGES_REQUESTED review (R2-1 Critical, R2-2): R2-1: routing the gate to the persistent ecs-qwen pool moved its CI_BOT_PAT gh api call onto a host with a shared, writable ~/.config/gh. A co-resident survivor could plant http_unix_socket there, receive the PAT, and forge the permission response downstream consumes as should_review. Prepend the same pins as qwen-autofix.yml's route/review-scan — export GH_HOST=github.com plus a private, guarded GH_CONFIG_DIR (a failed mktemp exits 1; the gate fails closed). No `unset GH_TOKEN`: it is this step's only credential. Witnessed with real gh 2.103.0: the as-written step leaks the PAT to a planted socket and approves a stranger; the hardened step denies with the socket untouched. R2-2: extend the checkout-free guard to the run: half — pin the step inventory to the single permission step and forbid `${{` spliced into any run body (event values must arrive via env:). Mutation-witnessed: removing the preamble and adding a run-only second step each red the test. Also correct the design-doc parity claim (both languages): the exposure matches review-scan only WITH these gh pins; document the hardening. * fix(ci): extend the ECS gate routing to triage and harden the gate pins Two batches, verified together before push: 1. Extend the command-gate routing to qwen-triage.yml's authorize (the /triage, /tmux, /verify sibling blind spot): the same issue_comment / workflow_dispatch disjuncts, and — because that job has three steps that spend CI_BOT_PAT on gh — the same self-contained GH_HOST + guarded GH_CONFIG_DIR preamble on each, per the repo's per-step convention. Raise the recorded workflow size baseline to match the added hardening. 2. Address round-3 review (R2-2 fix-induced, R3-1): the gate pins tested substring presence only. Now evaluate the comment-stripped live lines and pin the guarded mktemp form + fail-closed shape, require GH_HOST / GH_CONFIG_DIR exported before the first gh api, and forbid executing repository code via relative paths or interpreters (the pool cwd is the reused $GITHUB_WORKSPACE). Applied to both the review and triage gate tests. Mutation-witnessed: an unguarded one-liner, a relocated preamble, and an appended `node ./…` line each red the tests. Tests: qwen-pr-review-workflow.test.js 271, qwen-triage-workflow.test.mjs 131, ci-runner-routing.test.mjs 103 — all pass; prettier + ESLint clean. * fix(ci): supply RUNNER_TEMP to the triage gate replay tests; harden the gate pins R4-1 (Critical): the new ${RUNNER_TEMP} gh-config preamble is the first RUNNER_TEMP reference in the triage permission step, and three replay harnesses in scripts/tests/qwen-triage-workflow.test.js execute that body verbatim without setting RUNNER_TEMP — off-CI (npm run test:scripts) the fail-closed guard exits 1 before writing should_run. Set RUNNER_TEMP to each harness's existing mkdtempSync dir (the :1773 precedent); the workflow's fail-closed guard is unchanged. Verified: the file is red under `env -u RUNNER_TEMP` before and 203/203 green after; dropping the line reds it again. (The review named two harnesses; running the full file surfaced a third.) Also clear the round-4 deferred notes: - R4-2: the /verify ack comment no longer promises a receipt "even when the sandbox lane is backed up" — the gate now also queues with the pool. - R4-3/R4-4: the triage routing pin is now an exact expression equality, the step inventory is pinned, and PAT-bearing steps are identified by env GH_TOKEN rather than gh subcommand spelling. - R3-1: tighten the no-repository-code deny-list (quote boundaries + interpreter as a word) so `bash -c './x.sh'` no longer slips past; mutation-witnessed in both the review and triage gate tests. Tests: triage vitest 203 (off-CI), triage .mjs 131, review vitest 271, ci-runner-routing 103 — all pass; prettier + ESLint clean; size gate green. * fix(ci): correct the gate-routing narrative; close the round-5 verify gaps R5-1 / F1 (narrative): the motivating incident is a /resolve, whose end-to-end latency this PR does not change — resolve-pr stays on ubuntu-latest, so only the gate wait is removed. State this in both design-doc languages and the pinning test comment; the saving lands on /review, /triage, /tmux, /verify (pool-routed downstream). Correct the "cancelled without ever running" sentence to the run's timeline (resolve-pr started when the gate finished and was cancelled ~29m36s later). S1: the no-repository-code deny-list missed absolute-path interpreters (/usr/bin/node x.js) and versioned Python (python3.11 x.py); widen the regex (optional path prefix + python[0-9.]*) in both gate tests. Mutation-witnessed: both previously-surviving mutants now red, clean tree green. S2: each hardened gh step minted a $RUNNER_TEMP/*gh-config.* dir that nothing removed; add trap 'rm -rf "$GH_CONFIG_DIR"' EXIT to all four steps (RUNNER_TEMP survives between jobs on the pool). S3: record the exact final workflow sizes in .size-baseline. Tests: triage vitest 203 (off-CI, RUNNER_TEMP unset), review vitest 271, triage .mjs 131, ci-runner-routing 103 — all pass; prettier + ESLint clean; size gate green. * fix(ci): pin the no-repository-code invariant on every triage gate step The triage gate meta-test applied the relative-path/interpreter denylist only to PAT-bearing steps, so a repository-code execution line added to the credential-free budget step survived the suite (witnessed by mutation). The no-repository-code invariant is a job-wide safety premise of the ECS routing: even a credential-free step that runs a planted file leaves a survivor on the persistent host. Pin it on every step's comment-stripped live lines, and align both design-doc versions with the widened coverage and the witnessed mutation set. Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): correct the routing-matrix coverage claim for the triage gate The validation plan said the triage gate is pinned by the triage meta-test AND the ci-runner-routing matrix, but the matrix never reads either authorize job (it pins the sibling pool jobs). State the actual coverage in both language versions. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4d61487419 |
ci(release): only file issues on failure and reconcile stale ones (#15852)
* ci(release): file failure issues only for failures and close stale ones The VSCode companion and Chrome extension notifiers filed an issue when a job was merely cancelled, and nothing closed that issue once a later attempt shipped, leaving a live failure report against a released version. Drop the cancelled arm from both notifiers and add a success-gated reconcile-failure job to each, reusing the shared release step. That step now takes the title prefix and release version so all three release workflows can close the bot-authored issue an earlier attempt filed. Fixes #15840 * test(release): pin the version boundary when closing a stale issue A nightly sibling of the same release sorts first in the gh issue list result, so dropping the " on " suffix from the title guard silently closes another release's issue. The fixture now covers that ordering and the boundary mutant is killed. * test(release): tie each reconcile prefix to the title its notifier files The fan-out suites asserted `FAILURE_TITLE_PREFIX` against a hand-written constant, so renaming the notifier's `--title` left both green while the real reconcile would match no issue and the stale release-failure issue would stay open against a shipped release (QwenLM/qwen-code#15852). Read the prefix out of the notifier's own `--title` argument and assert it starts with the prefix the reconcile job passes, instead of restating the literal a third time. * test(ci): pin the version token and separator in the release title contract reconcile-failure reads a notifier title through startswith(prefix + version + " on "), but the suite only pinned the prefix. A notifier that dropped the version or separated it with something other than " on " closed nothing while both suites stayed green, which is a silent failure of the stale-issue cleanup (#15840). Derive the rest of the title from the notifier's own --title, accept the version variable with or without its :-unknown default, and assert the reconcile job reads the same variable the notifier writes. --------- Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: 2SinceJuly <[email protected]> |
||
|
|
a12d6946c2 |
fix(managed-agent): mask digit-led passwords and refuse an empty Harness user info in the upgrade command (#16352)
* fix(managed-agent): mask digit-led passwords and refuse an empty Harness user info
Follow-ups to the review items deferred on #16110 and #16175:
- A refusal hides everything up to a URL's last @, also when the URL
parses: a password that starts with digits and holds a / ? or # reads
as a port to both the URL parser and java.net.URI, so
http://svc:2024#Secret@harness:4170 printed whole. The --server-url
query refusal masks too.
- Without the server's verdict, start refuses an @ before the Harness
host (http://@host, http://:@host), which java.net.URI reads as user
information and the connector refuses, instead of restarting the
Harness for it.
- The health row and start show a status of 0, false or "" instead of
dropping it, and print the URLs they dial through the control-byte
neutralizer.
- A Session id with a lone surrogate fails its row by name instead of
"URI malformed".
- The Flyway reader test covers a path two modules both ship; the user
guide names verify --tenant's --actor condition, and the design docs
state the masking, status and id rules as applied.
* fix(managed-agent): neutralize the DEL and C1 bytes a quoted value keeps
JSON.stringify escapes C0 controls but not DEL or C1 bytes, so a refused
URL or a Session id the verify row echoes could carry U+009B, an 8-bit
CSI introducer, to the terminal, and the id was not bounded. The four
refusals that quote a URL and the id row now go through printable()
after JSON.stringify: C0 controls keep their escapes, DEL and C1 bytes
show as U+FFFD, and the quoted text stops at 200 code points.
* fix(managed-agent): print no password a --server-url carries, and stop predicting java.net.URI
Review round 2 on #16352.
- A --server-url with an @ left after the URL parser reads no user
information is refused: it is the rest of a password that starts with
digits, which the parser reads as a host and a port, so the phase would
dial the wrong host and print the password. check judges --server-url
the same way, since its plan repeats it.
- start's three outcomes and verify's health row print every URL with its
user information masked.
- With the server's verdict false, the refusal names a reason only where
the value shows one the server gives too: a user name or password, a
query or a fragment, or an @ in an authority after //.
- Without a verdict, start takes only a plain http(s) URL, a rule of its
own and narrower than the server's, instead of modelling how
java.net.URI reads user information: http:/@host, a backslash, a tab,
an underscore or a space no longer reach the Harness restart, and an @
in a path still does.
- The design record and the deployment guide say so.
* fix(managed-agent): correct upgrade URL guards and plan output
* fix(managed-agent): keep refusing an @ in --server-url, and spell plan words
|
||
|
|
222ca313e2 | test(ci): harden TUI workspace reconciliation (#16033) | ||
|
|
5890329390 |
fix(ci): probe the host egress proxy so mainland release hosts can build the sandbox image (#16281)
The docker-container driver's buildkitd inherits neither the runner's proxy env nor the host network namespace. On ecs-qwen-hz1-17/18 the DNS answer for registry-1.docker.io is poisoned, so the pinned node:22-slim resolution dies with a direct-dial timeout before any application step — five consecutive image-build failures (#16039, #16162) that forced ECS_RELEASE_HOST_RUNNER_ENABLED=false. Probe the fleet-managed proxy marker and inject network=host plus env.*_PROXY driver opts only when the proxy answers a live registry probe. Hosted and HK runners have no marker file, so the probe emits nothing and their builds keep the default direct route. Verified live on hz1: unopted builder reproduces the incident, opted builder resolves the pinned digest for both platforms. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
f387c3a75a |
fix(ci): recreate git metadata before the publish-triage checkout (#16494)
* docs(ci): state the real publish-triage safety argument The publish-triage header comment claimed a 'clean runner', but the job routes to the persistent ECS pool like publish-tmux and runs without a container. The guarantee that actually holds is trusted-code-only: a sparse checkout pinned to github.sha, never agent-provided code, and a publisher that imports only node builtins plus one relative helper, so a reused workspace offers no bare-specifier node_modules to hijack. Reword the comment to say that, per the non-blocking note in the #16468 re-review. Comment-only change; no job, timeout, or behavior delta. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): recreate git metadata before the publish-triage checkout The patrol closeout on the comment rewrite checked the claims against the file and found two gaps. First, publish-triage was the only job reaching actions/checkout on the shared pool without recreating the workspace .git: an earlier agent run owned that workspace and could have planted hooks/config that checkout's git commands would execute in the one job holding the write PAT. Add the same Recreate runner Git metadata step the triage/tmux/verify jobs run before their checkouts, and add publish-triage to the qwen-runner-checkout-workflow matrix that behaviorally executes it (removal witness: deleting the step turns the new entry red). Second, narrow the header: drop the 'it is safe there' certification in favor of naming the controls, and replace 'sole writer' — the triage job's reaction and lifecycle host steps also write with the PAT — with 'the only path from the agent's drafts to GitHub'. Design pair updated in both languages. Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): keep the new step comment off the PAT-holder claim The publish-triage step comment and the design pair said the checkout runs in "the job that holds the write PAT", which is the same overstatement this PR removes: the triage job's reaction, status comment, and finalize step also write with that PAT. Name the publisher as what it is instead, so the header's new parenthetical and the design prose agree. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): pin the publish-triage safety claims and derive the checkout guard Addresses the remaining review findings in this PR. R1-2: publish-tmux's banner still claimed a clean GitHub-hosted runner while its runs-on selects the ECS pool; reword it to the controls that actually hold (checks out nothing, runs no repository code), routing untouched. R1-4: the publisher's bare-specifier-free import closure is load-bearing for the pool argument but nothing pinned it; add a meta-test that scans the closure and matches it against the sparse-checkout list (witness: adding import 'yaml' reddens exactly that test). R2-2: the runner-checkout guard's hand-typed job list is what missed publish-triage; derive the qwen-triage entries from pool-routed jobs that check out, so the next such job reddens with no test edit (witnesses: a new pool checkout job without the reset, and deleting publish-triage's reset, both redden). R2-3: pin publish-triage's ownership-restore fallback chain with assertUnconditional (witness: deleting the sudo fallback reddens). R2-1: the design pair's Files-affected list still said the runner-checkout test needs no change; correct both languages and narrow the pinned-by parenthetical to what each suite actually pins. Co-authored-by: Qwen-Coder <[email protected]> * test(ci): make the new pins layout-independent, floored, and complete Round 3 found three holes in the pins added by the previous commit, all in the guards themselves. R3-1: specifiersOf used a single-line pattern, so Prettier-wrapped imports were invisible in both directions — a wrapped bare specifier kept the closure test green while the pool job would die ERR_MODULE_NOT_FOUND, and a wrapped relative one dropped a needed entry from the expected sparse list. Span newlines, hoist the scanner to module scope, and fixture-pin it (wrapped relative, wrapped bare, and the publisher's own multi-line node:fs import); those three fixtures were verified red against the old pattern and green after, and the sparse comparison stays exact equality. R3-2: the derived job set had no floor, so rewriting one pool job's runs-on into a vars lookup silently dropped its row; add an arrayContaining floor that names the lost job while still admitting new pool jobs with no test edit. R3-3: the ownership test's name promised the full fallback chain but assertUnconditional matches chmod unanchored, so deleting the sudo chmod arm stayed green; pin that arm in this test rather than the shared helper, which has five other call sites. Witnesses: wrapped bare import reddens the closure test, a vars-lookup runs-on reddens the floor naming publish-triage, and deleting the sudo chmod arm reddens the ownership pin — each with byte-level restore. Co-authored-by: Qwen-Coder <[email protected]> * test(ci): prove the sparse checkout by loading it, not by scanning syntax The APPROVED review deferred three findings against the closure guard, all instances of one root cause: the guard modelled JS module syntax with a regex. Proven on the shipped code — a 1164-character wrapped bare import left it green (the 400-char bound), and an out-of-tree helper turned the byte-offset slice into the garbage entry /.github/scripts/elper.mjs. Replace the scanner with the condition the pool actually creates: materialize exactly the sparse-checkout list into a temp tree, assert no node_modules exists above it, and import the publisher there. Sufficiency is the load succeeding; necessity is each .mjs entry omitted making it fail; the owner policy is tied by resolving the publisher's own new URL literal and requiring that exact path in the list, instead of an unanchored substring plus a hard-coded entry. This removes the scanner and its layout fixtures rather than patching them again — wrapped imports of any length, prose that looks like an import, and out-of-tree helpers are all covered by the load itself. Witnesses: the long wrapped bare import that was previously green now fails, as do a short bare import, an out-of-tree helper, a helper dropped from the list, a stale extra entry, and a moved owner-policy path; each with byte-level restore. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e99286813e |
fix(ci): preserve Maven suite results across cache write-back failures (#15274)
* fix(ci): scope the sdk-java test lock to the cache write-back (#15262) The Managed Agent fast tier can fail without reporting a test result after waiting out the per-host SDK Java lock. #15128 scoped the MariaDB lane's lock to its cache write-back, but the test job still held the same lock across the SDK and Managed Agent Maven suites, so a long full-tier suite could starve the fast tier on that host. Seed the test job's Maven repository from the host cache, run both suites unlocked from that job-local copy, and take the ticketed host lock only for the cache write-back. The write-back children close fd 9 so a cancelled job's survivor cannot retain the lock. Pin the new order and the write-back fd closures in the workflow tests, and update the fair-lock design docs and the workflow size baseline. Fixes #15262 Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): keep sdk-java cache write-back best-effort (#15262) * fix(ci): keep the sdk-java gate result across the cache write-back (#15262) The write-back runs after the suite, so the step's exit code must come from the suite: both lanes now track the Maven status, demote only a lock-wait timeout with a step-summary record, and redden when the queue itself is unusable. The wait drops to 1800 s because it guards only the bounded copy; the seed stops hiding cp's diagnosis while staying quiet on a fresh host; the design docs record the head-of-queue stall the shorter hold does not bound. * fix(ci): revert the sdk-java lock helper to the PR footprint (#15262) The previous round changed .github/scripts/sdk-java-lock.sh, which this PR never touched; the deterministic gate rejects that expansion. Restore the helper and keep both call sites on the pre-round `if !` shape, retaining the 1800 s wait and the step-summary record. Drop the status-2 pins and tests that only existed for the reverted helper, and record the unusable-queue gap as a follow-up in the design docs. The timeout-returns test stays. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): de-alias every resolver tracking file and guard an unusable lock queue (#15262) * fix(ci): de-alias Maven install metadata in the sdk-java seed (#15262) Co-authored-by: Qwen-Coder <[email protected]> * test(ci): restore the per-job Maven seed binding (#15262) * fix(ci): keep sdk-java resolution metadata out of the host cache (#15262) The seed drops maven-metadata-*.xml hardlinks, but the write-back still published job-local metadata with cp -n, where the first publisher's copy could never be refreshed. Exclude it at both write-back sites and pin the seed and write-back name sets to each other. Also witness each suite-status exit with an exit-1 case, restore the lock test's combined-stream assertion, and bring the design docs' acceptance criteria and fault arms up to date. * test(ci): carry the deferred-status helper into the sdk-java slices The tolerated-skip arms now call mvn_report_failed_suite, which the step defines above the lock block. Every slice that starts at lock_setup_fault() or at the write-back therefore ran bash -e without the definition and died with 127, which read as a code failure rather than a missing prelude. * docs(ci): clarify remaining sdk-java cache hardening scope * fix(ci): bound failed MySQL cache waits and exclude resolver residue Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): clear inherited resolver state in the Maven cache seed prune The seed de-alias prunes carried -links +1, which restricted deletion to the hardlinks cp -aln created in this job. RUNNER_TEMP survives across jobs on the pool, so a previous job's mutable resolver state (nlink 1 *.lastUpdated, _remote.repositories, resolver-status.properties) survived in the job-local repository and could redden an otherwise green Maven lane. Drop -links +1 from all four seed prunes so inherited state is cleared again; the write-back's -links 1 delta filter is unchanged and the seed stays best-effort (2>/dev/null || true). Move the deAlias test pin with the fix and add a regression witness that plants single-link markers in the job-local repository before the seed body runs and asserts they are gone (red with -links +1 restored, exit 1; green with the fix, on macOS and Linux). Re-record the sdk-java.yml .size-baseline entry (153970 bytes) and synchronize both design-doc languages, including the nine-coordinate set the main merge brought in. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Qwen Autofix <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
dd4515ca27 |
feat(ci): isolate triage agent credentials and run all triage on ECS (#16468)
* feat(ci): isolate triage agent credentials and run all triage on ECS The triage agent carried a write PAT, so comment reruns and external fork-PR triage had to stay on ephemeral hosted runners while hosted capacity is tight and the ECS pool has headroom. Wire the token-isolation follow-up the workflow already documented: the agent reads GitHub with the job's read-only GITHUB_TOKEN and expresses every write as a draft; a separate publish job validates and executes drafts with the bot PAT, using one publisher script shared with local /triage runs. Contain the agent job, launch the CLI directly behind the hardened loopback model proxy, and move the deterministic maintainer resolver into the publisher. With no write credential in the agent environment, route every triage trigger to ECS behind the existing kill switch, and retire author_can_write. Markers, bot identity, and the finalize workflow's approve-on-green protocol are unchanged. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): bump triage CI budget and address triage review findings The new publish-triage job crossed the .ci-budget ratchet (7->8 jobs, 280->290 declared timeout-minutes), aborting Lint & Static before any lint or helper test ran. Bump the budget entry, mirror the artifact-name fallback expression on the upload side so an early-death run's drafts stay addressable, and enforce SKILL.md's process-label rule at the publisher chokepoint (welcome-pr, maintainer, help wanted, good first issue) with fail-closed tests, aligning the issue-workflow instruction that previously contradicted the rule. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): harden the triage draft pipeline after an adversarial audit Six findings from a multi-round pre-push audit, each with a mutation witness: kill lingering agent-user processes before the drafts strip/upload so a detached child cannot re-plant symlinks in the race window (upload-artifact follows them and RUNNER_TEMP is host-shared; unkillable survivors drop the draft set deny-by-default); lstat the draft containers themselves in the publisher preflight, since a symlinked bodies/ redirects the realpath containment base; reject cross-kind actions (review/close-pr on issues, close-issue on PRs); install util-linux for runuser as the sibling lanes do; make the mktemp'd proxy-wrapper directory traversable by the agent user so the gh/git wrappers do not silently fall out of its PATH; pin the artifact-name symmetry, the kill-before-strip ordering, and the toolchain install line. Design pair updated in both languages. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
8660f75430 |
fix(ci): refresh host assets on older PR checkouts (#16476)
* fix(ci): refresh host assets on older PR checkouts Co-authored-by: Qwen-Coder <[email protected]> * style(ci): format host asset regression cases Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
478c2f87ab |
feat(ci): make flaky-patrol rerun and no_action decisions visible (#16459)
* feat(ci): make flaky-patrol rerun and no_action decisions visible The CI Failure Patrol posted only a hidden HTML marker when it re-ran a flaky failure or could not classify one, so maintainers saw a red PR with no explanation and clicked Update Branch — advancing the head, dismissing approvals, and re-running the whole CI for a failure that was already being handled on the same commit. Post a short bilingual notice alongside the marker for both decisions: the rerun notice states the transient cause and that no Update Branch is needed; the no_action notice asks for manual review before rerunning or updating the branch. The deterministic-failure comment is byte-identical, and the post-success reset marker stays hidden. Pin the visible notices, the marker round-trip through dedup and the per-head action count, and reason escaping for all three actions. * fix(ci): strip bidi overrides from flaky-patrol notice reasons An adversarial audit of the visible patrol notices found that bidi control characters (U+200E/U+200F, U+202A-U+202E, U+2066-U+2069) passed through the reason escaping untouched. The reason is classifier output derived from untrusted CI logs, and a right-to-left override can visually rewrite the very sentence maintainers trust when deciding whether to update a branch. Drop those characters in the shared escaping helper so all three visible decisions (and the deflake issue signature) are covered, and pin the stripping for each action. Removing the strip line fails the new test. * fix(ci): cover the Arabic letter mark in the patrol bidi strip An escalated audit round compared the strip class against the full Unicode Bidi_Control set and found U+061C (Arabic letter mark) missing; it can reorder adjacent Arabic-script text in a visible notice. Add it to the class and extend the per-action pin payload, so the class now matches the complete Bidi_Control set. Removing just U+061C fails the pin test. * fix(ci): collapse newlines in patrol reasons and pin the audit gaps The verification round found that a newline plus a line-start fence in a classifier reason opens a block-level code fence in the visible notice: the folded Chinese block and the tracking marker render as visible code. Collapse newlines and their surrounding whitespace to a single space in the shared escaping helper, keeping every reason single-line. Close the coverage gaps the same round measured: feed all twelve Bidi_Control code points individually, pin the English-before-fold and Chinese-inside-fold placement of the deterministic comment, and assert the fold's closing tag in all three notices. Witnesses: removing the collapse, narrowing the bidi class, dropping U+200F alone, swapping EN/ZH, or dropping the closing tag each fails the suite; a marked rendering probe shows the fence swallowing the fold before the fix and a clean render after; benign comment bodies remain byte-identical to base. |
||
|
|
ee1a9b1e1e |
fix(ci): move audio prebuilds to macOS 15 (#16464)
Co-authored-by: luoyun <[email protected]> |
||
|
|
7cb46d9fed |
feat(hooks): add FileChanged notifications for local sessions (#13969)
* feat(hooks): add FileChanged notifications for local sessions * fix(ci): preserve shell gate coverage and trim transcript data * fix(ci): flush debug prompts and isolate git fixtures * fix(hooks): respect ACP watcher ownership and skill registration * test: stabilize hosted recovery and failed spawn fixtures * fix(ci): budget the complete hosted integration fork * fix(runtime-broker): accept MariaDB CSI guard metadata * test: align schema baseline and hook dispatch waits * fix(ci): allow hosted shared fork to finish all classes * fix(hooks): preserve file watching across errors and cwd changes * fix(ci): isolate reference archive extraction per run * test(ci): keep reference archive checks dependency-free * fix(hooks): constrain recursive watches and repair CI * test(web-shell): synchronize verified connection return * fix(ci): correct runtime broker callback indentation * test(web-shell): match the managed workspace registration label Keep the remote workspace registration smoke checks in sync with the service label added by unified workspace navigation. Preserve exact text matching and all registration and replay assertions. * fix(ci): reduce standalone runtime fixture overhead * fix(hooks): ignore implicit atomic-write temporary files * test(managed-agent): recover initial node port conflicts * fix(hooks): observe parents before discovery anchors disappear * test(managed-agent): await stream cleanup before recovery teardown * test(managed-agent): align hosted shell fixture capabilities * test(managed-agent): let OS choose initial harness port * fix(ci): restore runner-local Java cache permissions * test(runtime-broker): use native execution profile paths * test(managed-agent): bind valid workspace upload probes * test(managed-agent): reuse the upload probe digest fixture * test(managed-agent): bind hook fault listeners to available ports * test(managed-agent): align the native profile path fixture * test(managed-agent): Wait for projection refusal retries * ci: Prepare daemon Java cache before Node preflight * test(managed-agent): check channel model calls after delivery * fix(hooks): observe explicitly registered symlink targets * test(managed-agent): separate consumption from relay snapshots * test(managed-agent): target tamper injection by resource key * test(managed-agent): exhaust attestation EOF retry in fault gates * fix(ci): bound RocketMQ release downloads * test(tui): await the restored assistant before capturing history * fix(ci): use a checksum-pinned RocketMQ mirror * fix(ci): provision missing browser test fonts * fix(ci): quote the font probe environment * fix(ci): prepare browser fonts without elevation * test(core): compare complete prune reports without ordering * fix(ci): repair only runner-local Java tool caches * test(managed-agent): qualify fixture JDBC templates * fix: isolate reconnect hooks and cancellation test fixtures Skip chat initialization so MCP reconnect cannot re-enable file hooks, and keep cancellation fixtures isolated from the background worktree scanner. * fix(ci): tolerate a missing Java cache helper on resumed releases Resuming an existing release checks out a tag that may predate the helper, which failed all five Java jobs before setup-java ran. * fix: finish the main merge for harness deadlines and workflow size Use the renamed settle-timeout accessor in the merged hosted-harness tests, and record sdk-java.yml's new size after the resumed-release cache guard. * style(managed-agent): restore import grouping in merged verification tests * test(managed-agent): require consumed child result mirrors * test(web-shell): center repository settings capture --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: yungsen.hys <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
1b1f4455d9 |
ci: build the PR tree once and shorten the required path (#16450)
* ci: build the PR tree once and shorten the required path The no-AK integration gate, the four web-shell smoke shards and Lint & Static each rebuilt the tree test_build already uploads, and the build cache rarely helped: most runs logged "29 of 29 workspaces changed" because the arch-keyed cache was split across the mixed x64/ARM64 pool and a prefix restore returned the newest entry instead of the base commit's. - test_build restores the build its base commit saved (ci-build-cache.mjs key --at), drops runner.arch from the key, and runs the serve fast-path closure and core subpath checks against the built tree. - The no-AK integration gate unpacks that tree and runs as three shards; integration_no_ak_result keeps the required check name. - The web-shell smoke unpacks the same tree instead of building; consumers recopy the opentui native assets for their host (host-assets). - The standalone runtime regression moves from unit shard 1 to shard 4. - A java_only profile skips the TypeScript lanes for changes confined to the Java packages; Lint & Static runs the scripts/tests suites that name them. - Drafts skip the integration gate and the browser smoke; ready_for_review starts the full run. * ci: check the settings schema in test_build, which has the core dist Lint & Static only had core's dist because the serve fast-path check built it; with that check moved, Generate settings schema failed to import it. * ci: keep the standalone regression on unit shard 1 Moving it did not shorten shard 1: the regression runs in the side lane, in parallel with the CLI and core suites, and shard 1's time is its CLI slice (576 s against 147 s on shard 4 for the same 181 files). That imbalance is left to duration-based sharding. |
||
|
|
1a20792cfa |
ci: route the no-AK integration gate to ECS for every pull request (#16132)
* ci: route the no-AK integration gate to ECS for every pull request
Same trust class as lint_and_static (2026-11-02 extension of the
2026-10-31 decision): the integration step executes PR code, but the
lane carries zero secrets by construction — every model key is emptied
at the step and the job's only token is the read-only checkout grant.
The gate's step-level runner.temp HOME becomes run-unique and is
cleared before use (the R2-1 lesson: RUNNER_TEMP survives across jobs
on this pool). test and integration_cli keep the association-gated
ubuntu_runner. Mutation-verified: runs-on revert, fixed HOME, and a
real key in the PR-code step each red the routing battery.
* fix(ci): isolate no-AK HOME from the first step; value-pinned zero-secrets
Round-2 review findings: (1) Critical — the gate-scoped HOME missed the
lane's actual fork-code start: checkout's local action and the install
(lifecycle scripts plus scripts/prepare.js, un-skipped) ran under the
runner's real shared home with a shared COREPACK_HOME — the exact
cross-job poisoning channel the lint acceptance closed. Mirror lint:
first-step GITHUB_ENV run-unique HOME + COREPACK_HOME inside it +
shared verified XDG cache + always() reclamation. (2) Critical — the
zero-secrets guard was name-based and missed odd-named secrets; enforce
by VALUE (every non-path env key empty, no secrets.* anywhere in the
gate). (3) runs-on literal pin tightened (timeout line also mentions
lint_runner). Docs: EN single-lane phrasing, stale merge-queue claim,
2026-11-02 paragraph rewritten for the first-step shape. Six mutations
red (first-step removal, shared corepack, odd-name secret, no cleanup,
runs-on revert, fixed HOME).
* docs(ci): correct the enforcement-order claim and stale trust-basis note
Round-2 review: the doc claimed the routing battery runs before
dependency install — false (HELPER_TESTS imports yaml, runs after
install, and not at all on github_ci_only PRs' own runs); and the
ci.yml trust-basis comment still described the round-1 gate-scoped
HOME shape. State the actual timing and detection latency, and the
shipped first-step shape.
* fix(ci): scrub ANTHROPIC_AUTH_TOKEN at the no-AK gate; honest cache naming
Round-3 review: the Anthropic SDK's auth resolver back-fills
ANTHROPIC_AUTH_TOKEN from env, so a runner-account residue would survive
into fork PR code on the pool — add it to the gate's scrub list (pinned
by both suites). The 'Configure persistent npm cache' step name promised
persistence the per-run HOME removes; rename it and record the accepted
cold-install cost (shared pnpm store stays a follow-up).
* fix(ci): scrub ANTHROPIC_AUTH_TOKEN at the no-AK gate; honest cache naming
Round-3 review: the Anthropic SDK's auth resolver back-fills
ANTHROPIC_AUTH_TOKEN from env, so a runner-account residue would survive
into fork PR code on the pool — add it to the gate's scrub list. The
value whitelist only iterates present keys, so the deletion mutation
escaped it; pin the key's presence-and-empty in the battery and the
28-key literal list. The 'Configure persistent npm cache' step name
promised persistence the per-run HOME removes; rename and record the
accepted cold-install cost (shared pnpm store stays a follow-up).
* test(ci): pin the no-AK grant, HOME template and reclamation path
Three assertions in the no-AK routing battery proved less than they read:
- the job's `permissions` was never inspected, so the read-only-checkout
grant that justifies routing untrusted fork code onto the pool could be
widened without a red test;
- `create.includes('HOME=')` is satisfied by `COREPACK_HOME=` and
`XDG_CACHE_HOME=`, so deleting the real HOME/USERPROFILE publication kept
the test green;
- the cleanup only had to contain the `qwen-no-ak-home-` prefix, so a path
that dropped `-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}` would still pass
while removing a directory nothing used.
Pin the grant by value, the publication by its printf template, and the
reclamation by the exact run-unique path the create step builds.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw48g5gafx
* test(ci): pin the realized HOME values in the shared ungated lane loop
R2-1: assert the create `home=` line, the gate QWEN_HOME and the cleanup
command by value, and require the exact set of `$GITHUB_ENV` writers. An
`echo` stand-in for the cleanup, a `-v2` create path and an appended second
HOME writer all kept the battery green under containment checks.
R1-2 / R1-1: hoist those pins into the shared `ungated` loop so
`lint_and_static` gets them too. Its create step was pinned by
`includes('HOME=')` (satisfied by `COREPACK_HOME=`) and its cleanup by
`includes('qwen-')`, so a dropped `HOME=%s` or a retargeted `rm -rf` survived.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw4h0sl6g9
* test(ci): red on later republishes of the isolated HOME keys
R3-1: the GITHUB_ENV equality guard read step 0 only, so a later step that
republished HOME reverted the run-unique home by last-write-wins while every
assertion stayed green — the isolation would silently stop applying to the
steps that run PR code. Scan the remaining steps for isolated-key
$GITHUB_ENV writers and for step-level HOME/USERPROFILE/COREPACK_HOME/
XDG_CACHE_HOME overrides, leaving the npm cache step's own key a legitimate
later write.
Mutation-verified on this head: appending printf 'HOME=%s\n' ... to
integration_no_ak's 'Configure per-run npm cache (self-hosted)' step and
adding a step-level `env: HOME` each turn the suite red (103 pass -> 102
pass / 1 fail); reverting restores 103/103.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw4ng9wvgi
---------
Co-authored-by: Qwen-Coder <[email protected]>
|
||
|
|
738c4bbf7e |
fix(serve): bound command snapshot replay memory (#16413)
* fix(serve): bound command snapshot replay memory Suppress unchanged command snapshots and retain only the latest full skill details so periodic refreshes cannot grow daemon heap indefinitely. Bound replay rings by serialized bytes while preserving ACP live delivery and reconnect IDs, and expose retention diagnostics. * fix(serve): avoid redundant replay seed serialization Size only retained complete command snapshots when seeding replay history, and forward the replay byte limit through the development daemon launcher. Pin snapshot accounting and lifetime eviction counters with independent regression assertions, and document the count/byte retention bounds and conditional command notifications. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
45f71b3714 |
fix(ci): apply the apt-mirror probe to the release docker lane too (#16278)
* fix(ci): apply the apt-mirror probe to the release docker lane too The release docker integration lane builds the same sandbox image as the E2E leg but never selected an apt mirror, and it prunes the builder cache on every run so its apt layers are always cold. On mainland pool hosts that rebuilt against the throttled deb.debian.org path (11-45 kB/s) — the same defect #16214 fixed for E2E, left unfixed here because it was out of scope (#16271). Extract the probe (apt_mirror_probe_rate / select_apt_mirror) and a new apply_apt_mirror helper into a shared .github/scripts/apt-mirror-probe.sh, sourced by both run-e2e-tests.sh and run-release-docker-integration.sh. Each calls apply_apt_mirror before build:sandbox, so a mainland host builds against the VPC intranet mirror (6-9 MB/s, verified for E2E) while a healthy default is left untouched. The Dockerfile APT_MIRROR ARG already exists from #16218. Contract tests read the shared probe and pin the candidate loop and the source statement (not substrings the comments also satisfy); both callers must apply the probe before the build. Mutation-verified: removing the release call, a candidate, or a source line each reddens the suite. * fix(ci): carry the apt-mirror probe into the shard-retry test rig run-e2e-tests.sh now sources its sibling apt-mirror-probe.sh at startup. The shard-retry meta-test copies only run-e2e-tests.sh into a temp dir, so the source resolved to a missing file and 'set -e' aborted before any shard ran — all eight cases saw zero npm calls. Copy the probe file alongside the script in the rig so the source resolves; sourcing only defines functions (no network at source time) and the sandbox:none path never calls apply_apt_mirror, so the rig stays hermetic. Witness: CI at |
||
|
|
36c8b34222 |
perf(core): load the MCP client's Google auth providers on demand (#16242)
* perf(core): load the MCP client's Google auth providers on demand mcp-client imported GoogleCredentialProvider and ServiceAccountImpersonationProvider statically, so every process that loads the MCP client, including each resident Managed Runtime worker, also loaded google-auth-library whether or not any server uses Google authentication. createTransport is already async; import each provider only for a server configured with it. Add transport tests for the service-account impersonation provider, which had none in this file. On Linux x86_64 an idle worker loads 0.24 MiB less JavaScript and holds 0.9 MiB less private memory. * fix(core): ask for a restart when a Google auth provider chunk is missing Now that the MCP client imports its Google auth providers on demand, the bundle puts them in separate chunks. A background update that replaces dist under a long-lived process made the next connection fail with "Cannot find module", wrapped as a connection error for the server. Move the restart guidance that contentGenerator already gives for its lazy provider chunks into utils/errors (getModuleNotFoundError, wrapLazyModuleLoadError) and apply it to both provider imports. The original error stays as the cause. Add google-auth-library to the ACP static closure guard, so a static import of either provider fails check-serve-fast-path-bundle again. * test(core): isolate the lazy Google auth chunk tests in their own file Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
5d45b42940 |
feat(verify-pr): give the sandboxed round the PR's review threads (#16300)
* feat(verify-pr): give the sandboxed round the PR's review threads The sandboxed /verify round had no token and no review context. Its only inputs were the metadata snapshot and, on a follow-up round, the previous report. On #13652, Critical R1-26 was confirmed and then marked fixed three days before a round that published merge-ready without mentioning it. Eight hours later the review bot, at the head that round had certified, still could not rule out its residual. The verify job's trusted resolve step now writes review-threads.json beside pr.json. It holds every review thread, resolved ones included, since resolving is the author's claim, together with each reviewer's standing CHANGES_REQUESTED review. It comes from one paginated GraphQL query reshaped with jq. The snapshot is best-effort: on failure no file is written and the step warns. verify-pr-prepare.mjs sends the same query and writes the same shape for the local path. It removes a previous copy first, so a failed fetch cannot leave a stale file. A test runs the workflow's block verbatim and prepare's transform over the same pages and requires equal output. The skill adds report item 4, Existing review findings. Each term the verdict turns on is computed from snapshot fields: - a thread is blocking when its first comment is tagged Critical, Blocker, P0 or P1 by an owner, member or collaborator; - a blocking thread is open when it is unresolved, or when that comment belongs to a standing change request, resolved or not. That is the rule GitHub's review decision applies. An open blocking thread left unmeasured rules out merge-ready, and with nothing failed the verdict is inconclusive. Findings at fail 0 would publish as "passed with non-blocking findings". Other blocking threads are re-measured as budget allows, and the verdict line counts those left unmeasured. Change request bodies are listed but do not hold the verdict, because their severities are prose. The duty is also a line in the scope budget. A missing file reads as unknown, never as "nothing open". The file adds 2396 bytes, so the size baseline records the exact 378242. * test(verify-pr): pin the review snapshot's jq guard, copy order and odd answers - The CI/local parity test runs the step's real jq program. Like the file's four other jq cases, it now skips when jq is missing, instead of failing one assertion later with a message that names neither jq nor the missing tool. - The local recipe must clear the cached review-threads.json before it copies prepare's file. The test pinned the three lines but not their order. With rm after cp, every local round would read the review state as unknown. - prepare's two other failure arms are now driven through the CLI: an answer that is not JSON, and pages without a pull request. Each must leave no file and say why. An empty state there would read as "nothing open". * test(verify-pr): drive the review snapshot through the shared gh harness The review-snapshot case had its own copy of the executed-step harness: a temp dir, a stub gh on PATH and GitHub's bash flags. makeGhHarness exists so that fidelity contract lives in one place. The factory now records every call's argv, returns its temp dir, and has an empty-stdout arm for paginated reads, GH_STUB_EMPTY_LIST. The snapshot case runs through it. Its failure arms are the factory's GH_STUB_FAIL_LIST and GH_STUB_EMPTY_LIST. Deleting the shared stub's fail arm, its empty-stdout arm or its argv capture turns the case red, and so does recording only the first argument. |
||
|
|
edb094fea3 |
test(managed-agent): gate lifecycle-v2 close with a stopped worker (#16123)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
0cc37bf444 |
feat: add Qwen subscription support across CLI, VS Code, and Desktop (#16378)
* feat(subscription): add development login, models and web search * feat(subscription): refine account flows and host integration Align desktop and VS Code subscription UI, add account logout and confirmed plan display, and map subscription thinking parameters. Remove simulation code and obsolete native authentication paths, consolidate design documentation, and cover cancellation and recovery boundaries. * fix(subscription): preserve request errors and remove unused compatibility * fix(subscription): refine command visibility and client logout flows * fix(subscription): simplify account actions and desktop sign-in feedback * docs(subscription): clarify sign-in and provider guidance * feat(subscription): support switching accounts across clients * fix(subscription): align account actions and open plans on demand * fix(subscription): restore model selection after logout Filter subscription-only commands consistently across terminal renderers, preserve model catalog error classification, and display the subscription authentication name consistently. * fix(subscription): unify status authentication labels across clients * fix(subscription): preserve provider connections across client logout Restore configured providers after subscription logout across CLI, Desktop, and VS Code. Keep unrelated connections and conversation history, synchronize model state silently, and return to an unconfigured chat when no provider remains. * fix(subscription): correct native client logout state * feat(subscription): enable sign-in without development mode * refactor(ui): 优化会员登录成功提示和界面交互体验 - 移除订阅欢迎提示相关状态和行为,简化 UI 状态管理 - 替换登录成功提示为四行摘要,包含邮箱、套餐、模型及常用命令提示 - 更新国际化文本,支持新的登录成功摘要内容 - 取消订阅模式下输入框下方的命令提示行,改为在登录成功摘要中展示 - 移除多余的测试用例,新增登录成功消息的单元测试校验 - 优化 AppHeader 组件,不再显示订阅启动重复提示 - 调整 Composer 组件逻辑,删除订阅提示相关副作用及渲染条件 - 修改历史消息显示组件以适应新的成功提示格式 * fix(subscription): enable Flash reasoning controls in shared UI * fix(subscription): prioritize authorized models and simplify login dialogs * fix(subscription): preserve gateway errors and request IDs * fix(subscription): synchronize account state across clients Preserve model selections after sign-out, synchronize account commands and model catalogs, and retain explicit browser sign-in flows across CLI, Web Shell, and VS Code. Cancel subscription background agents on logout and normalize expired refresh credentials without SDK error wrapping. * fix(subscription): reset default model on explicit sign-in Choose the default served model when explicitly signing in to Qwen Subscription, preferring Max over a previously saved Flash selection. Preserve normal startup behavior and other provider flows, and cover CLI and workspace activation with regression tests. * fix(vscode): refresh models after successful sign-in * fix(web-shell): hide account actions during automatic sign-in * fix(web-shell): align model details and auth keyboard navigation * docs(subscription): revert previous guide changes * fix(core): reuse subscription catalog for same-login model switches * docs(subscription): document Qwen membership sign-in * fix(subscription): unify quota notices and preserve provider switching Carry structured subscription quota details through chat and search errors for localized CLI, Desktop, and VS Code display. Keep other providers available when subscription discovery fails and refine sidebar account actions. * fix(web-shell): align Chinese subscription entry title with CLI * fix(subscription): open plans for saved non-member accounts * fix(web-shell): show subscription commands after desktop sign-in * fix(subscription): reauthorize explicit account sign-ins * fix(auth): add model switching hint to subscription success * docs(auth): list Qwen OAuth as the first option * docs(models): simplify subscription sign-in guidance * docs(models): remove legacy OAuth from supported auth types * docs(subscription): link membership references and remove obsolete allowance note * fix(subscription): preserve session until reauthorization succeeds * fix(subscription): use pre for browser authorization and plans * fix(subscription): use production endpoints * test(vscode): fix editor message test formatting * fix(subscription): preserve unauthenticated session startup * fix(cli): classify subscription auth during ACP preflight * fix(subscription): scope activation to the current project --------- Co-authored-by: luoyun <[email protected]> |
||
|
|
08f1843c22 |
feat(mods): run the prompts a Mod submits in the Ink terminal (#16373)
* feat(mods): run the prompts a Mod submits in the Ink terminal * test(mods): run a Mod's prompt in a real Ink terminal * fix(mods): stop a Mod's prompts when the person stops a turn in Ink - Esc or declining every tool a turn asks to run drops the Mods' queued prompts and refuses new ones until the person sends a prompt again. The stopped turn's turn.complete hooks run after the stop, and a call a hook does not await reaches the host after the hook returns, so dropping the queue alone let such a hook start the next turn. $.turn.abort still keeps the queue. - A Monitor update waiting out its minimum interval no longer holds a Mod's prompt, which a Monitor printing within every interval held for good. - A prompt put back after losing the idle moment runs once the winning submission settles, since that one may start no turn. - The dual-output sidecar gets the Mod's text and a mod_prompt system message, as a stream-json client does, instead of the frame. - The guide's capability table lists Ink for a Mod's turn events. * fix(mods): drain Mod prompts after any settle and strip a stopped one A Mod prompt queued while a submission that starts no turn held the session (a cron's Mod command, a notification dropped before sending) stayed queued with the session idle: the drain re-ran on settle only for a prompt it had put back. Re-run it whenever a Mod prompt waits. When the person stops a Mod prompt's turn before it answers, the framed prompt stayed at the end of the model history, so the next prompt reached the model behind it. The cancel info now says the turn ran a Mod's prompt, and the handler strips it as it does a Goal continuation's. * fix(mods): run a waiting Mod prompt once a Goal lets go of the turns A Mod prompt queued while a Goal owned the session's turns waited for the next submission to settle or another prompt to be queued. The Goal often releases its turns after the session is already idle (its verifier accepts, its last turn is released), so the prompt could sit until some unrelated event. The drain now also re-runs on the Goal runtime's broadcasts while a Mod prompt waits. The test no longer queues a second prompt to wake the drain; it changes the Goal snapshot and broadcasts, and fails without the fix. |
||
|
|
de6ccb4e3c |
feat(serve): allow configuring the Conversations working-directory root (#16105)
* feat(serve): allow configuring the Conversations working-directory root * test(serve): register Conversations root E2E in no-AK CI * fix(serve): address Conversations root review feedback * fix(serve): clarify Conversations root configuration errors --------- Co-authored-by: Jiankai Xiao <[email protected]> |
||
|
|
a818643533 |
feat(mods): let a Mod submit a prompt of its own in headless runs (#16302)
* feat(mods): let a Mod submit a prompt of its own in headless runs
$.prompt.submit({ text, asUser? }) raises prompt.submit through every hook
but the caller's, with the Mod as its origin, and queues what arrives
beneath them. The headless run takes it once idle and runs it as a turn of
its own: UserPromptSubmit hears it, turn.start and turn.complete fire, and
the transcript records it with the provenance mod_prompt and the Mod's
name. Unless the Mod passes asUser, the model reads the text inside a block
that names the Mod and says the person did not type it.
The call resolves once its prompt is queued, as next(e) does for a typed
prompt. The Mods' prompts start at most 8 turns before the person sends a
prompt. The terminals do not run these prompts yet: there the method is not
listed and a call resolves to a drop.
* fix(mods): queue a Mod's prompt once, outside its caller's lifetime
- Run the hooks a `$.prompt.submit` raises outside the calling hook, so a
call the hook does not await still queues after the hook returns.
- Queue what last arrived beneath the hooks once they have all answered,
so a hook that calls `next(e)` twice queues one prompt.
- Drop an empty or whitespace-only text, from the call or from a hook.
- Discard Mod prompts that have not started when a headless run ends, so a
stream-json session does not run them on the client's next message.
- Escape every `<` and `>` in the framed text.
- Recall memory for the Mod's text rather than the frame, keep a Mod's
prompt out of the session title (in its turn's place) and out of the
commit attribution's prompt count.
* fix(mods): keep the title, SDK output and turn count right for Mod prompts
- Mark a Mod's prompt as `null` in the title projection, not `''`, so it
no longer switches a session whose prompts recorded no display text to
an empty projection, which stopped auto-title and `/rename --auto`.
- Tell a stream-json client of a Mod's prompt: its text as a user message,
then a `mod_prompt` system message naming the Mod, before the turn runs.
- Reset the Mods' turn count for the person's slash commands too.
- End a Mod turn that a duplicate provider call stops as an error on the
shared loop, as on the legacy loop and for the main turn.
- Refuse a prompt submitted while /clear or /resume ends the session, and
start the new session with nothing queued and the count at 0.
* fix(mods): keep charging a hook while the other Mods hear its submit
A $.prompt.submit call outlives its caller, yet it lent the caller's
budget for the whole submit chain, so a hook that did not await it ran
unbudgeted until the other Mods' prompt.submit hooks answered. It now
runs as the void notices do, charging the caller as it goes.
The frame also no longer says the Mod sent its prompt while the session
was idle: most are sent mid-turn and wait for the session to be idle.
* fix(mods): run a Mod command's submitted prompt and keep it out of the title
A Mod command in a headless run always answers with a message, and that
branch returned before the drain, so a prompt the command submitted with
$.prompt.submit was discarded while the call had resolved as queued. The
reply is now shown without a turn of its own and the run goes on to the
drain, ending with one result.
A session whose prompts recorded no display text drew its title from the
conversation, which held the Mod's framed prompt. The recorder now keeps
the text the model read for each Mod prompt, and the conversation-based
title leaves those messages out.
* fix(mods): keep a Mod's prompt within its payload and its command's reply in view
- Drop a submitted prompt past half a Mod payload, from the call or a
hook's rewrite, before it is queued, so a queued prompt is never
reported as failed; an input too large for the bridge now rejects.
- Print a Mod command's reply in text output, and give the worktree notice
the command would have carried to the first request the run sends.
- Leave an `asUser` Mod prompt out of the title keys, which matched a
prompt the person typed alike, and keep the keys least recently used.
- Make Goal evidence leave a Mod's prompt out explicitly.
- Pin the documented turn limit and the tool-result guard in tests, and
add a built-CLI test of `$.prompt.submit` against the fake server.
- Docs: six steps, the non-goal narrowed, the hook docs and the budget
paragraph naming Mod prompts.
* test(mods): run the $.prompt.submit integration test in the no-AK job
|
||
|
|
ef9c891b8f |
fix(ci): clear the pinned corepack cache before every install (#16351)
* fix(ci): clear the pinned corepack cache before every install The Linux test shards have been dying seconds into Install dependencies with MODULE_NOT_FOUND on the cached pnpm's bin/pnpm.mjs since #16328 pinned COREPACK_HOME to runner.temp (main runs 41876940141, 41889761157, 41927887452; six different self-hosted hosts). The pin assumed runner.temp is per-run, but self-hosted runners keep _work/_temp across jobs — the lint job's isolated-HOME step already documents this. corepack trusts an existing cache entry without re-verifying its files, so a partial pnpm tree left by an earlier job fails every later job on that host. Clear the pinned cache at the start of the shared install body in all three jobs that copy it, enforcing the per-run contract the pin documents. The guard keeps the reset a no-op where COREPACK_HOME is unset (test_build) or already run-unique (lint). The test step still reuses the cache install warms in the same job, and poisoned hosts self-heal on their next job without manual cleanup. Pin the reset and the install/test COREPACK_HOME match in the workflow meta-test, and bump the size baseline for the added lines. * fix(ci): scope the corepack cache reset to the shard-owned path Review found two over-deletion shapes in the nonempty guard: a runner that exports COREPACK_HOME would lose its entire shared cache in the build job, and hosted lint would lose the cache its pnpm-store step had just warmed, re-exposing the install to a registry outage. Match only the exact shard-owned path instead; the build and lint copies become unconditional no-ops and the three install bodies stay byte-identical. Add an execution test that runs the shipped guard fragment under a scrubbed environment across five cache shapes: it kills the dead-guard mutant the string pins survived, and a widened-guard mutant dies on the shared-cache survival cell. State the incident as observations with the corruption source undetermined, and correct the stale per-run rationale beside the pin. |
||
|
|
8a7c0d17d8 |
fix(managed-agent): find the Flyway scripts in the module jar the server ships them in (#16175)
* fix(managed-agent): find the Flyway scripts in the module jar the server ships them in Since #15871 split the server into Maven modules, its Flyway scripts ship in BOOT-INF/lib/qwen-managed-agent-persistence-*.jar rather than in BOOT-INF/classes/db/. The upgrade command's check read only the classes, so on every archive since then its database row warned "cannot compare the Flyway scripts (no Flyway script found in ...)", and a release that changed or reordered a script the installed database ran went unflagged. migrationScripts() now also reads db/**/ at the root of each nested jar in BOOT-INF/lib/. Spring Boot stores those uncompressed, so their directories are read in place; a compressed one, which Spring Boot would not load, is skipped. A script keeps its path as its name wherever it ships, so one moved from the classes into a module compares as unchanged. The reference distribution workflow now reads the scripts out of the exact archive it built, so a later change of layout fails there rather than turning check's row into a standing WARN. * fix(managed-agent): read a Flyway script shipped twice with different contents as unreadable The reader keys the scripts on their classpath path, and the nested jars give that key a second source. Two copies of one path with different contents left whichever the central directory listed last in the map, so the database row compared one arbitrary copy, while the class loader would hand Flyway the other. Matching copies are still one script; differing ones now make the jar unreadable, which the row reports as "cannot compare the Flyway scripts". * fix(managed-agent): name both places that ship a differing Flyway script, and pin the local extra field - When one script path ships twice with different contents, the error now names the two places, BOOT-INF/classes or the nested jar, so the operator need not unpack the jar to find the second copy. - The test zips can carry a local-header extra field, as archivers add for timestamps or alignment, and the nested-jar case reads entries past one, so dropping that term from dataStart fails the suite. |
||
|
|
b9e20830f7 |
fix(verify-pr): post flaky demotions on closed PRs and pin the blank class (#16135)
* fix(verify-pr): post flaky demotions on closed PRs and pin the blank class The closed-PR skip from #16050 also caught the flaky variants of the cancelled, failed-before-report and results-unavailable notices, and the report body of a round that wrote no report.md. Those render ❌ and, on a merged PR, name non-deterministic tests now on main, so they post unless this run's own report already carries the demotion. - publisher: the closed-PR skip leaves FLAKE_VERDICT=flaky alone, except beside this run's own report - publisher: the gates line says its tests are "not counted individually above", since each suite run now counts as one assertion (zh: 不逐条计入上面的断言) - checker tests: pin the blank class on their own (whitespace refused, U+0085 and U+FEFF accepted); before, only the shared parity table in the workflow suite caught a revert to trim() - SKILL: rewrap the over-long local-path line * fix(verify-pr): scope re-run advice to open PRs and pin the gate condition The results-unavailable notice can now land on a merged PR, where the verify lane refuses `@qwen-code /verify`: both variants now offer the re-run only while the PR is open. The flaky exemption keys on the gate verdict, which holds only because the gate step runs on a prepared round; a test now pins that condition. * fix(verify-pr): skip a flaky notice only beside a report that carries it On a closed PR, a flaky notice with no live status of its own to finalize is skipped beside this run's own report, as a duplicate of the demotion that report carries. That report can come from an earlier attempt whose gate did not diverge. A re-run attempt whose own live status is missing (that post is best-effort) then lost its ❌ to the run log, beside a report that never showed it. A flaky verdict now adds a verify-flaky marker to the header, and the closed-PR skip drops a flaky notice only when this run's own report carries that marker in its header. * test(verify-pr): pin the own-comment lookup's initializers The publish step runs under `set -u`, and a failed comment listing or bot identity lookup leaves OWN_ID, OWN_SUBSTANTIVE and OWN_FLAKY at their initial values. Dropping any of those three lines kept the suite green: no case made either lookup fail. On such a run the step would instead abort with "unbound variable" and post nothing. The upsert stub can now fail the listing or the identity lookup. Under either failure, a report posts fresh rather than patching a status it cannot see. A flaky notice on a closed PR also posts, because the lookup cannot tell whether this run's report already carries the demotion. Deleting any one initializer now fails the test on its own unbound variable, which the harness now prints with the failed exit status. |
||
|
|
9123d5ee04 |
fix(managed-agent): refuse every URL fetch refuses and say why a health probe failed (#16110)
* fix(managed-agent): refuse every URL fetch refuses and say why a health probe failed start and verify refused a listener URL only when URL.canParse rejected it, so a --server-url without a scheme (proxy.test:8443), with a user name or password, or on a port the Fetch standard blocks (6000, 10080) passed the check and then failed every probe until --timeout. They now refuse each of those before anything runs, and a test checks that the runtime's fetch rejects every URL the check refuses. A probe that is sent and still fails now says what it got: start's timeout names the last probe's answer and verify's health row names its own, such as "HTTP 503, status DOWN", "self-signed certificate" or ECONNREFUSED, where both said only "not UP". The listeners() comment names the reachesHarness option verify passes. * fix(managed-agent): refuse a Harness URL that is not http or https in upgrade start start checked the Harness URL with new URL() alone, so an opaque value such as proxy.test:8443 parsed with no host and no port: start then waited out --timeout for the Harness to listen on port 80 of an empty host. It now refuses any Harness URL that is not http or https before it starts a unit, as the preflight would fail it later anyway. The probe-answer test also runs the runtime's own fetch against a port nothing listens on, so the ECONNREFUSED wording comes from a real error rather than a constructed one. * fix(managed-agent): keep the endpoint's answer when the deadline cuts start's last probe short start's last health probe gets the 1 ms the deadline leaves it, so against a real endpoint it times out before any answer arrives, and the timeout message reported that abort instead of what the endpoint had been answering, such as HTTP 503, status DOWN. A probe that times out on a budget the deadline cut below the per-probe cap no longer replaces an earlier answer; one that runs out of its own 5 seconds still does, since the endpoint stopped answering. The test rig's fake fetch now awaits a response function, so a test can model an endpoint that answers late and honours the abort signal. Also pins the IPv6-zone diagnosis to a percent inside the brackets. * fix(managed-agent): treat a health body the deadline stalls as a cut-short probe too A health endpoint that sends its headers and then stalls the body made response.json() reject with the signal's TimeoutError. That branch did not report timedOut, so start's guard took the probe for an answer, and a probe the deadline cut short replaced an earlier HTTP 503, status DOWN with "HTTP 200, The operation was aborted due to timeout". The body-read branch now reports timedOut as well. The last-answer test gains two real-socket cases: a body that stalls after its headers keeps the earlier answer, and a refused connection after an answer replaces it, which pins the timedOut half of the guard. The design doc says when the 5-second limit counts as the probe's own, and that any other failure replaces an earlier answer. * fix(managed-agent): sanitize health text and align upgrade refusals Address review R1-1..R1-5 on the upgrade URL/health change: - R1-1: neutralize terminal control bytes (C0/DEL/C1 -> U+FFFD) in the endpoint-derived health answer before it is interpolated into start's timeout refusal and verify's health row, so a queryable endpoint cannot move the cursor, split a row, or write the clipboard (OSC-52). - R1-2: the Harness URL refusal reuses fetchRefusal when the value does not parse, naming an IPv6 zone exactly as --server-url does. - R1-3: start's timeout labels the retained value 'the last answer', matching the design doc, since a deadline-cut probe keeps an earlier answer. - R1-4: pin verify's URL refusal (listeners runs it with reachesHarness:false) with a test asserting it refuses before any probe. - R1-5: tear down the 'closes' test server via t.after so a failing assertion cannot hang node --test to the job ceiling. Tests: 63 pass; R1-1/R1-2/R1-4 mutation-witnessed (printable no-op, refusal gated on reachesHarness, zone reason reverted each redden their pin). Prettier and ESLint clean. Co-authored-by: Qwen-Coder <[email protected]> * test(managed-agent): exercise the health sanitization over a real socket Add a real-scenario pin for R1-1: a local HTTP server serves a status field carrying cursor moves, a newline and an OSC-52 clipboard write, and verify runs against it through the runtime's own fetch — the real health() -> printable() -> row() -> out() path, not the rig's fake. Asserts the printed health row keeps no control byte while the readable text survives. 64 tests pass. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): mask a refused URL's password and name why verify's API reads failed Found on a real systemd deployment (real control plane, Harness, MySQL, TLS and nginx): - A refused --server-url was printed as typed, so a URL with a password, which fetch refuses, put that password in the output. The refusal now prints it as ***. - verify's session and turn rows still said only "fetch failed" where the health row above them named the cause, such as "self-signed certificate in certificate chain". API reads now take the same cause. - Text the API hands back (an error reason, a body that fails to parse, a Session ID, an event type) reached the session and turn rows with its control bytes; it is now neutralized as the health answer is. The design doc says the probe answer's control characters show as U+FFFD. * fix(managed-agent): show a health status that is no string as its JSON printable() ran String() on what the endpoint returned, and String() throws on a valid JSON object whose own toString is no function, such as {"status":{"toString":1}}. health() is not inside a try, so on a real deployment verify exited with "Cannot convert object to primitive value" before it printed its health row, and start's probe loop stopped at the first probe. A value that is no string is now shown as its JSON, which is also what the endpoint actually said; an API error reason gets the same. * fix(managed-agent): make the upgrade reports total and bounded, and refuse only what a phase dials Review round 5 traced its findings to two shared causes, fixed at the source rather than one site at a time. Endpoint text in the reports (printable, withoutCredentials): - printable() no longer throws on any parsed JSON value: a status nested deeper than JSON.stringify() recurses showed a bare "Maximum call stack size exceeded" from verify and left start's server running without its guidance; it is now shown as [unprintable]. - It also stops at 200 code points, cut after the control bytes are gone and never inside a surrogate pair, so a long status cannot scroll the report away. The item count on the session row goes through it too. - withoutCredentials() masks the whole user information, so a token in the user-name position is masked, and masks it textually when the URL does not parse. The Harness refusal masks its URL as well. URL checks (listeners, the Harness guard): - A phase is refused only over a URL it dials: start and verify probe the health endpoint, and only verify --tenant reads the API. An API on a port fetch refuses behind a separate management port no longer stops start. - The Harness URL is checked as the preflight checks it, an http(s) origin without user information, a query or a fragment, so start refuses it before it restarts a running Harness rather than after. * fix(managed-agent): take the Harness verdict from the preflight and check the API URL only when verify reads it - verify decides whether its session row reads the API before it checks the listeners, so a signed deployment, or --actor without a trusted actor header, is no longer refused over an API URL it never dials. - The configuration resolver now prints the preflight's own verdict on the Harness URL, read untrimmed as java.net.URI reads it, and start refuses on it before restarting the Harness: a host with an underscore, an empty user name or a padded value no longer restart the Harness only to fail the preflight. - withoutCredentials masks everything up to the last @ of a value the parser rejects, so a password holding / ? or # stays hidden. - printable counts code points in one pass and keeps only the head, so a huge endpoint answer costs no copy of itself. - The request an API error names is bounded like the rest of the row. * fix(managed-agent): print the Harness URL untrimmed, as the server parses it A padded qwen.managed-agent.harness.base-url fails the preflight, and the start refusal now shows the padding instead of a value that looks valid. * fix(managed-agent): judge the Harness URL only where the server reads it The resolver passed on the preflight's verdict on the Harness base URL with no regard to the Harness's state, so start refused a value that a disabled or pooled Harness leaves unread: the preflight skips its harness row before reading it, and the server never dials it. The verdict is now "skip" there. start refuses on the preflight's grounds (user information, a query, a fragment, or anything java.net.URI reads differently) only on a "false" verdict, and its own reasons only word that verdict. It still refuses a URL that is not http or https, since it waits for the Harness there. Without a verdict, as when the configuration does not resolve, the preflight judges the URL once the Harness is up. verify now checks the type of the values the API chose before using them. Building a route from a Session id, matching an event type, or reading an event sequence coerced the value first, and that throws on an object such as {"toString":1} or on an array nested deeper than the stack, so printable() never got to show what came back. A Session without a string id fails its row with that value, an event type that is not a string fails the turn row, and a sequence counts only as a number or a numeric string. The start tests now check every run's output for the password, not only the last run's. * fix(managed-agent): judge a pooled Harness URL, and read only lists from the API The previous commit gave "skip" for a pooled Harness as well as a disabled one, since the preflight skips its harness row in both before reading the base URL. A pooled server still reads that URL, though: each member's QwenHostedHarnessConnector is built from it and refuses it by the same rule as the preflight. The pool's refresh drops a member whose connector refuses it, so such a pool accepts no Session while the server reports UP. Only a disabled Harness gets "skip" now. A test pins the verdict to whether the connector builds, for a single Harness and for a pool. Since the verdict is now the server's rather than only the preflight's, the refusal says "the server does not take". verify reads Sessions, items and events only as lists. Data that is not a list fails its row and says what came back: iterating it would throw, or walk a string, and its length could be any value. A listed entry of 0, "", false or null fails the session row instead of being reported as no Session. An event sequence moves the cursor only as a whole number, and only forward: Number() reads "Infinity", "0x10" or " 4 " as numbers. The user guide now says that, when the configuration does not resolve, the preflight judges the Harness URL after the Harness starts. * fix(managed-agent): keep start's own Harness URL rules without a verdict When the configuration does not resolve there is no verdict. The previous commits then let start accept any http(s) Harness URL and left the URL to the preflight. A pooled preflight never reads it, though, and every pool member's connector refuses user information, a query or a fragment, so the pool would start and accept no Session. Without a verdict, start refuses those itself again, since the server refuses them wherever it reads the value. A "true" or "skip" verdict still asks nothing more, and the generic wording is kept for a "false" verdict. HarnessConfigurationTest now builds a pool through the real member factory and shows that the single base URL decides whether a member is accepted. The pooled half of the agreement test checked nothing the single half did not, so it is gone. verify now requires every page to carry its data as a list: a body that is no object, or one whose data is null or missing, fails the row instead of reading as no Session. A Session id of "", "." or ".." fails, since fetch resolves dot segments and an empty id reads the list. Only terminal: true ends a turn. The docs no longer say the server takes only an origin, since a path is fine. They say which component refuses the URL, and they word the sequence rule as the code applies it. * fix(managed-agent): sort the test's static imports, and refuse a --server-url query The Mockito static import sat between two JUnit ones, so the verification module's ImportOrder check failed. The static block is now sorted. start and verify append /actuator/health and the API paths to --server-url, so a query or a fragment there took the paths in. With http://proxy.test/?a the command probed /?a/actuator/health, and fetch dropped a fragment along with the path after it. Such a URL is now refused up front. A listed Session entry with no id is now shown as itself rather than as "undefined". A health answer whose JSON parse error quotes control bytes is pinned to come out sanitized. --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
2b9c4a98f4 |
fix(ci): pin COREPACK_HOME for Linux test shards on cold-HOME runs (#16328)
The Linux unit-test shards set HOME to a fresh per-run directory for isolation. scripts/tests/corepack-warmup.js (vitest globalSetup) spawns `corepack pnpm --version` before any worker forks, and on a cold HOME corepack downloads the pinned pnpm from registry.npmjs.org. The ECS egress intermittently times out on that fetch (ETIMEDOUT to 104.16.9.34), which kills the shard after every test file has already passed — today that produced 7 PR-facing failures across random shard numbers (2/8, 5/8, 3/8, etc.), all on the same self-hosted host. Install dependencies runs before the test step and warms the corepack cache under the runner's real HOME, but the test step's HOME override makes that cache invisible. Pin COREPACK_HOME to a per-run path in both steps so they share the same cache without touching the HOME isolation. Per-run, not persistent: corepack does not re-verify extracted entries on cache hit, so a shared cache would be a poisoning channel (the same reason lint_and_static keeps its COREPACK_HOME per-run). Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
a5fec03684 |
fix(ci): fail Java lanes before disk exhaustion (#16068)
* fix(ci): fail Java lanes before disk exhaustion Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): gate the MySQL pool lane instead of a hosted job The disk floor gate landed in hosted-harness-mysql, which runs on ubuntu-latest, so `runner.environment == 'self-hosted'` is never true there and the step was dead code. The job that actually shares the ECS pool disk - mysql-integration, two Maven suites plus a per-leg MySQL container - stayed ungated. Move the step to mysql-integration, immediately after its checkout so the sdk-java-workflow pin still holds. This also clears the three Hosted real-process gates pins, which reject a conditioned step among the setup steps that precede managed_dependencies in hosted-harness-mysql, and reflow the new test to the repo's prettier style. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvya43o17i * style(ci): drop the blank line left behind in hosted-harness-mysql Moving the disk floor gate out of hosted-harness-mysql left a stray blank line between its checkout and 'Set up Java'. Restoring it makes the whole workflow diff additive on mysql-integration only, so 'hosted-only jobs are unchanged' holds byte for byte and an A/B census of that job reads clean. Co-authored-by: Qwen-Coder <[email protected]> * ci(sdk-java): record the real workflow size after the disk-floor gates The six #16003 disk-floor gate steps grow sdk-java.yml from 154074 to 155650 bytes. main had already drifted 3742 bytes past the recorded 150332 — inside the 4096 allowance, but it left only 354 bytes of ratchet headroom, so this real growth tripped Lint & Static. Record the file's actual size rather than shrinking the gates: each one is a three-line step calling .github/scripts/check-disk-floor.sh, and the workflow test pins all six gated jobs. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw02ewzoa2 * fix(ci): derive the disk-floor corpus from runs-on and narrow the broker claim Address the two blocking review findings on the Java disk-floor gate: R1-1: the meta-test skipped any job without the gate and compared a closed six-name list, so a newly pool-routed lane that forgot the gate stayed green, and a neutered gate (continue-on-error) kept every assertion true. Derive the corpus from each job's runs-on (same hazard as the setup-java corpus), require gateIndex > -1 per pool-capable lane, and assert the gate carries no continue-on-error. The derived set is the same six jobs today. Verified by mutation: adding continue-on-error to a gate reddens it, and renaming a gate (ungated pool job) reddens it with 'expected -1 to be greater than -1'. R1-2: the step comment claimed RUNNER_TEMP covers the broker store, but the absolute free-KiB floor cannot express the broker's used-ratio refusal (a 952 GiB volume at 94% used still has ample free bytes). Narrow the comment to the gate's real scope (near-full filesystem / inode exhaustion, #10035) and state the broker disk-threshold investigation stays out of scope, matching the PR description. Bump .size-baseline for the comment change (155650 -> 155888). sdk-java workflow meta-test 117 passed / 5 skipped; eslint exit 0, prettier clean; check-workflow-size.sh passes. * fix(ci): calibrate the Java disk floors and fail closed in the meta-test Review round on this PR found the gate could not fire in the two shapes it exists for, and that the meta-test could go green through the same drift it was added to prevent. R1-2: every lane ran the shared 2 GiB / 100k-inode default. The #16003 incident was a used-ratio refusal — the broker reported 0.94 while the 952 GiB volume still had tens of GiB free — so no absolute floor can express that threshold, and 2 GiB is below what these lanes write: a host with 4 GiB free passed the gate and then died mid-suite. Give the five heavy lanes the repo's build-sized 8 GiB floor, leave flyway-migrations and the shared value alone, and add an opt-in DISK_FLOOR_MAX_USED_PCT arm to check-disk-floor.sh for the two broker lanes (90%). R2-1/R2-2: the corpus came from `runs-on` text and skipped jobs outside it, so a lane routed through an expression or a job output shipped ungated while the suite stayed green, and nothing asserted the hosted-only jobs carry no gate. Fail closed: an explicit hosted-only allowlist, a gate required for every other job, a total-count assertion, and a negative assertion on those two jobs. Pin the per-lane floors so dropping one is a test failure. Verified: sdk-java-workflow suite 117 passed, 5 skipped; it goes red when a gate is deleted, when one is added to a hosted job, and when a floor is removed. check-disk-floor suite 12 passed, including four new ratio cases. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> Co-authored-by: qwen-code patrol <[email protected]> |
||
|
|
b383efb854 |
fix(ci): widen the built-tree download window for eight shards (#16285)
* fix(ci): widen the built-tree download window for eight shards Eight shards now pull the same unit-test-tree artifact at once, so the download cap has to clear the blob's tail latency rather than the median. On main the slow host class took 4m52s, and two consecutive runs expired twice at the five-minute cap before the unpack ran, which failed the required Linux check with no test involved. Raise both attempts to fifteen minutes and pin the pair in the build-to-shard handoff contract. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): size the shard download cap from the measured tail Review flagged that the outgoing comment claimed a pool host had stalled "for over a quarter of an hour", which the new fifteen-minute cap then contradicts. That claim came in with the sharding commit and was never tied to a run; neither incident can prove how far past five minutes the tail went, so the comment now states only the measured distribution. Also correct the number: the slowest completed shard took just under five minutes, and it was a different shard that expired, on two consecutive runs. Add the missing shard-side ceiling contract, mirroring the build job's: every blocking step cap has to fire before the job cap, because a cancelled job skips the failure-gated disk-pressure collector. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
a85370b38f |
feat(cli): run the Ink TUI's model rounds on the shared loop's round (#16206)
* feat(cli): run the Ink TUI's model rounds on the shared loop's round With experimental.unifiedAgentLoop on, every model round of the Ink TUI goes through the shared loop's runModelRound, which collects the tool calls, drops those of an attempt a retry or fallback replaced, and turns the client's terminal events into a stop reason. The TUI keeps its stream processor and its tool step. Adds the Ink parity suite. Part of #15327. * test(cli): tighten the Ink parity suite and fix the no-AK script pin - add the suite to the no-AK script pin - check each run's request count, and through the debug log that only the run with the setting on sends its rounds on the shared loop - compare the system prompt, the tools offered and recorded usage - run each scenario once, without the root config's retries, with a source pin - describe the suite as it is in the design doc - say in the setting's description that the Ink TUI sends its model requests through the shared loop |
||
|
|
56fec7f3f5 |
ci: shard the Linux unit tests and web-shell smoke, and reuse cached builds (#16133)
* ci: shard the Linux unit-test lane eight ways behind one build The Linux Test job ran 45-59 minutes: a ten-minute prepare build, then the CLI and core suites back to back. A new test_build job runs the prepare build once and uploads its outputs; eight shards install without the build, unpack them and run --shard=N/8 of every Vitest suite. Test (ubuntu-latest, Node 22.x) becomes an aggregate that passes only when the build and every shard pass. * ci: record ci.yml's growth from the test_build prelude test_build carries the same pre-checkout cleanup, checkout and install prelude as the test shards; ci-platform-lanes.test.js pins the two copies identical. * ci: record the Linux lane's two new jobs in the PR-stage budget test_build and the test_ubuntu aggregate add two jobs; the declared timeouts drop from 305 to 295 minutes. * ci: shard the web-shell E2E smoke four ways The smoke lane ran 44-46 minutes on every recent PR, about 33 of them in the browser tests at two workers, and is the longest lane once the Linux unit tests are sharded. Each shard runs --shard=N/4 of the smoke; the transcript gate runs on the first. web-shell E2E Smoke (ubuntu-latest, Node 22.x) becomes an aggregate over the shards. * ci: bound the unit shards' tree download and retry it once A pool host stalled 17 minutes in download-artifact while the other shards took under a minute. * ci: rebuild only the workspaces a change reaches scripts/ci-build-cache.mjs hashes each workspace's tracked build inputs (tests excluded) with its workspace dependencies' hashes and the shared root inputs. test_build and the web-shell smoke shards restore the newest cached build and rebuild only workspaces whose hash changed. A push to main always builds from scratch and saves, so a pull request starts from a clean build of main. * docs(ci): describe the incremental build cache * ci: keep the build cache path the same on every pool host actions/cache versions an entry by its path; under runner.temp the path differs per host, so a run could never restore another host's save. * ci: save the ARM64 build cache from the smoke lane, and run the aggregates on the shards' runners The smoke shards run on the ARM64 pool, which test_build may not, so the first shard saves the clean build it made when its restore found nothing. The two aggregate checks queued 9 and 13 minutes for a hosted runner after their shards finished; they now take the shards' runners. * ci: bound the sharded test step and skip shards when the build fails Address the first automated review round on the Linux sharding lane: - Gate every build-consuming shard step on `test_build` success. `!cancelled()` stays true when a needed job fails, so a failed build still started all eight shards, each paying a full install before dying at the missing artifact and reporting artifact plumbing as its failing step. - Drop the test step cap to 45 minutes. The job cap is 60, so the old 110 could never fire; a step timeout keeps the diagnostic steps below it alive while a job timeout does not. - Keep the built-tree artifact for a week so a late "re-run failed jobs" still finds it, and correct the trigger comment that still named three post-merge jobs when five now accept push. Co-authored-by: Qwen-Coder <[email protected]> * ci: keep disk telemetry for a failed build and correct the design doc `test_build` installs with the disk sampler and then builds for up to its whole timeout, but the sampler dies with the install step's trap and no collector ran, so an ENOSPC build (or the OOM that took a shard host) kept no evidence. Give it the same failure-gated dump and last-step upload as `lint_and_static`, under its own artifact name. The incremental-lint design still said the test job keeps normal prepare behaviour; this lane now installs with `QWEN_SKIP_PREPARE=1` and unpacks the tree built once by `test_build`. Correct both language versions. Co-authored-by: Qwen-Coder <[email protected]> * ci: share the built-tree definition and the build preflight, harden the guards The archive step re-implemented `builtPaths()` from `scripts/ci-build-cache.mjs`, so the tree the shards unpack had a second definition that could drift from the one `pack` stores. A `paths` mode now prints that list for `tar -T -`, and a new test pins the four artifact-name literals that tie the producer to the eight consumers. The cached branch of `build` also hand-reproduced a subset of `scripts/build.js` and so skipped its ink-patch preflight: the same missing `frame-controller.d.ts` self-healed on a cache miss and surfaced as ~50 tsc errors inside ink's declarations on a hit. The preflight is now a function reachable as `scripts/build.js --preflight` and runs before both branches. The remaining review suggestions were all meta-test gaps: the standalone-runtime expression and the `test_build`/`test` prelude were guarded by nothing that compares them in order, `ci-build-cache.mjs`'s three CI modes were never executed by a test, and the infra-classification filter's step name was not joined to `ci.yml`. Each now has an assertion that goes red when the guard is removed. Co-authored-by: Qwen-Coder <[email protected]> * ci: bound the build steps so the failure collectors can run Round-2 review found four issues in the previous round's fixes and one gap in the new build tests. A job timeout cancels the job and skips the failure-gated collectors, so the `test_build` steps that can block now carry caps below the job's 45 minutes: the install, the build, the pack and the archive. The install caps are identical across `test_build`, `test` and `lint_and_static` so the prelude parity guard still compares the three copies field for field. A test pins every blocking cap under the job ceiling. The `--preflight` early exit was only asserted as a string, so it could be renamed with nothing going red while the cache-hit path silently ran the full build. A spawned case now copies `scripts/build.js` into the fixture and asserts the flag exits without generate, a workspace build or the schema step. The CLI mode tests write extension-less `#!/bin/sh` stubs and join PATH with ':', which cmd.exe cannot resolve; the Windows lane collects that file and downgrades its failure to a warning, so it would have carried a permanently red block. The suite is excluded on win32 alongside the other bash-fixture suites. The `test_build`/`test` intersection grew to 16 with the disk collector, but the floor stayed at 15, leaving exactly one shared step uncompared; the floor now matches the membership. The cache-hit-with-changes branch — the code that decides what is rebuilt and what is deleted — gains a case: a changed workspace is purged and rebuilt by directory, and an untouched workspace keeps what the cache restored. ci.yml's recorded size baseline is updated to the current file so the next PR gets the full growth allowance back. Co-authored-by: Qwen-Coder <[email protected]> * test(ci): keep cache fixture call logs outside the archived tree Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: 易良 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4d5e30e6f0 |
fix(test): wait for the RocketMQ port listener to clear after stop (#16252)
Aligns the parked-port release assertion with the script's own listener check: on a shared pool host a fresh bind probe can fail for a foreign socket or race the killed listener's asynchronous socket release (release run 41708821484, Scripts lane). The sibling case already uses waitForNoListener. Refs #16248 |
||
|
|
e1bf0a2c24 |
fix(ci): resume linter archive downloads (#16067)
* fix(ci): resume linter archive downloads Retry truncated actionlint and shellcheck downloads from the bytes already received instead of starting over on each attempt. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): resume only after the first linter download attempt The retry loop passed --continue-at - on every attempt, including the first one, right after the stale local copy was removed. A resume request against a missing file appends only the tail, so the archive failed its SHA-256 pin and the lane died. The PR's own test asserts the opposite: the first request must not resume, retries must. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvy7yxvq7f * fix(ci): bound linter download retries to the CI step budget Three review findings on the resume loop: - Five attempts at --max-time 90 plus 5/10/15/20s of backoff is 500s, which outlasts the 5-minute 'GitHub CI helper checks' step that runs this installer (ci.yml:1433). On a stalled asset host the fourth attempt was cut off by the step, so neither the final exit nor the same step's --actionlint/--yamllint/helper tests ran. Three attempts put the worst case at 285s, in line with the ~273s bound of the curl --retry 2 call this loop replaced. - curl resolves --continue-at - against a missing file to offset 0 and sends no Range header, so the old comment described a hazard that cannot happen and the first-attempt guard it justified was inert on the wire. The comment now states what the loop and the 33/36 branch actually do, and the guard is gone. - Neither the 33/36 rm -f nor the attempt cap was covered: deleting the rm -f and raising the cap to 99 both left the suite green. Two tests now pin them, each verified to fail under its own single mutant. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvymz2cq81 * fix(ci): state the real resume budget and name the failed archive Address the three review Suggestions on this PR's own new code: - Budget comment: 285s is per archive, and the steps that provision both archives inside one 5-minute step can still cut the second one off. Also drop the PR body's "up to five attempts", which never matched the three the code ships. - Terminal silence: the last attempt exits before the retry line, so a continue-on-error run ended on "retrying in 10s..." with no archive named and no exhausted budget. Echo that line, keeping curl's own exit status. - Range-refusal rationale: measured curl exits 33 and leaves the partial byte-identical, so the rm -f is load-bearing because every attempt would otherwise re-send the same offset, not because a second body gets appended. The stub now models that sequence (28 with a partial, then 33 untouched, then a clean full body). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw0d4pwgah --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
4593a3b8f2 |
fix(ci): run desktop smoke headless on rpm roots without Xvfb (#16211)
* fix(ci): give the RHEL 10 desktop smoke a headless display RHEL 10 removed xorg-x11-server-Xvfb from every repository: BaseOS, AppStream, CRB, extras and EPEL 10 contain no such build, so the rpm build-root dependency step failed one step after #16154 fixed the EPEL bootstrap (`No match for argument: xorg-x11-server-Xvfb`, run 41642580745, job 125940965630). Keep Xvfb for the Fedora 44 leg and give the RHEL 10 smoke a real display instead: a weston headless compositor with rootless Xwayland on top, since GTK3 has no headless backend and WebKitGTK always needs a display server. Export WAYLAND_DISPLAY before Xwayland is launched, wait for both sockets with a hard timeout, and pin the app to X11 so the bridge is actually used. Verified end to end on a Desktop Checks dispatch of this head (run 41652880267): the RHEL 10 rpm lane passes dependency installation and the packaged smoke, and the Fedora 44 lane keeps its xvfb-run path. The three workflow meta-tests that parse this file pass (161 passed, 1 skipped). * fix(ci): key the rpm display arm on the matrix and harden its meta-test Review round 2 findings on #16211: - R2-1: the display arm keyed on an artifact_suffix literal while the EPEL bootstrap it depends on keys on matrix.epel. Declare matrix.display ('xvfb' on Fedora 44, 'weston' on RHEL 10) and branch on that, so a leg added later picks a server instead of inheriting an arm by accident. - R2-2: the xvfb-run arm shipped a shell function through declare -f into a child bash without -e -u -o pipefail. Run the same npm command in both arms so both keep the step's error regime; assert no declare -f remains. - R2-3 / R1-4: bound the shared package list by the conditional that follows it instead of a prose comment, ignore comments when checking it, and assert the xvfb/weston arms, the weston socket name against WAYLAND_DISPLAY, and the Xwayland display number against DISPLAY and the X11 socket. * fix(ci): fail the rpm display arm closed and pin the smoke's wiring R2-1: the install arm keyed on `matrix.display` but defaulted every unknown value to the weston arm, which only works where the EPEL bootstrap above has run. Turn it into an explicit `case` with an error arm, and assert from the matrix that a non-Fedora root taking the weston arm has EPEL enabled, so a new leg with `display: 'weston'` and `epel: 'false'` turns the meta-test red instead of dying at `dnf install` in a release run. R1-4: restore the two pins the previous rewrite dropped (`weston --backend=headless-backend.so`, `XDG_RUNTIME_DIR`) and bind the rest to the step's structure rather than to bare substrings: the weston readiness wait must sit between the two launches, the helper must `return 1` on timeout, and the EXIT trap must reap both daemons. Each added assertion was checked against the mutation it names (all five turn red; the unmutated tree stays green). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw1dkg4zbx --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
b5975157d1 |
fix(ci): drop the healthy-default apt gate and prefer the VPC intranet mirror (#16260)
* fix(ci): drop the healthy-default gate so a fast apt burst cannot veto the mirror The merged probe (#16218) kept a third gate: override only when the default measured below 250 kB/s. On the mainland hosts the deb.debian.org path is bimodal — run 41743539611 on hz4-2 probed 372 kB/s (a fast 512 KiB burst), so the gate kept the default, and the sustained 200 MB transfer then throttled to 45 kB/s and hit the 60-minute cap again. Remove that gate. The two mirror-side gates already cover the cases it was meant to protect: the 1 MB/s absolute floor rejects a "slow vs slower" override, and the 4x factor keeps a genuinely fast default (the Hong Kong hosts) on the built-in source. The hz4-2 sample (default 372 kB/s, mirror 1.97 MB/s) now selects the mirror; the Hong Kong and both-slow shapes are unchanged. The contract test pins the floor, the factor, and the absence of any healthy-default gate. * fix(ci): prefer the VPC intranet apt mirror to bypass the capped public egress The public mirrors still traverse the pool's capped 100 Mbps public egress that throttles under concurrent load — the exact problem configure-maven-mirror.sh solves for Central by preferring mirrors.cloud.aliyuncs.com, which answers from inside the VPC and consumes no public bandwidth. Add it as the first apt probe candidate. Off the VPC (hosted runners, Hong Kong hosts) it is unreachable, the probe measures 0, and a reachable public mirror wins — so no host map is needed and the probe self-verifies on each build. |
||
|
|
aaf523cfc4 |
fix(ci): probe the apt mirror so mainland E2E hosts can build the sandbox image (#16218)
* fix(ci): probe the apt mirror so mainland E2E hosts can build the sandbox image The docker E2E leg builds the sandbox image per push under a SHA-scoped tag, so on any host whose image store no longer holds a recent build, the two apt-get layers are downloaded again. deb.debian.org serves the Hong Kong hosts at MB/s and the mainland-China hosts at 11-20 kB/s, which turns that build into 2-3 hours against the job's 60-minute cap. Over the past week 97 of 127 docker legs on the ecs-qwen-hz* hosts were cancelled this way, and the run reports `cancelled` instead of a verdict. Probe rather than pin a mirror or a host-name map: the build measures deb.debian.org and two domestic mirrors, and passes --build-arg APT_MIRROR only when the default is slower than 250 kB/s and a mirror beats it by 4x. The image keeps deb.debian.org as its default source, so release builds, local sandbox builds and the healthy Hong Kong hosts are unchanged, and no fleet inventory needs to be maintained. The probe's measurements are logged, so the next slow build can be diagnosed from its own output. Refs #16214 * fix(ci): require an absolutely fast mirror before overriding the apt source The relative guard alone only asked a mirror to be 4x faster than the default, so on a host where both endpoints were slow the mirror still "won" and moved the host onto a source it never needed. Require 1 MB/s in absolute terms too: below that the build is slow either way, and the extra cache key is not worth it. * test(ci): pin the apt-probe threshold values, not just the gate structure The new contract test asserted the gate variables and comparisons but not their values: mutating slow_default 10x up or fast_mirror 10x down left the suite green while flipping the both-slow host shape onto a still-slow mirror (review finding on #16218). Pin both literals with a word boundary — a plain substring pin would still match the 10x-up mutation — and witness both mutations reddening the suite before restoring the values. * test(ci): require the mirror ARG in both Dockerfile stages A single-match assertion survived deleting the runtime-stage ARG APT_MIRROR (R1-1, mutation-witnessed by review): the builder declaration satisfied the regex while the runtime apt layer silently kept the slow default. Count both declarations instead; deleting either stage's ARG now reddens the suite. |
||
|
|
a8e6c2ee57 |
ci: scope triage finalize concurrency to PR runs (#16070)
* ci: scope triage finalize concurrency to PR runs
Co-authored-by: Qwen-Coder <[email protected]>
* test(qwen-triage-finalize): compare the concurrency group with whitespace flattened
The group is a folded YAML scalar, so its rendered value keeps a newline
next to the more-indented continuation line. Pinning the literal string made
the assertion fail on the PR's own workflow file (Test (ubuntu-latest,
Node 22.x) at
|
||
|
|
b1cd4ac0c6 |
refactor(managed-agent): align shared MySQL schema conventions (#16143)
* docs(managed-agent): document database tables and columns * refactor(managed-agent): align shared MySQL schema conventions |
||
|
|
68c9fa27fb |
feat(verify-pr): add local Docker recipe with a host-side model proxy (#16077)
* feat(verify-pr): add local Docker recipe with a host-side model proxy A local /verify-pr round needs the same isolation the CI lane provides, but the skill only documented the boundary check, not how to run the model credential through it. Add scripts/verify-pr-model-proxy.mjs, a host-side relay that holds the real key and lets the container call the model with a per-run bearer token, mirroring the CI lane's inline proxy (POST /chat/completions only, nonce health check, per-chunk idle watchdog). The listen address is a flag so the proxy can bind the Docker bridge, and readiness is printed as one JSON line. The SKILL recipe pairs it with a docker run template: the token travels as QWEN_VERIFY_PROXY_TOKEN (not OPENAI_API_KEY, which the isolation gate refuses), and the container's provider entry reads it via envKey. The CI lane keeps its inline copy — the proxy holds the real key, so it must not execute a script from the PR-head checkout. Verified: 11 new pin tests plus all existing verify-pr suites (120 passed), and a real docker smoke — the gate passes inside node:22-bookworm with the proxy token, refuses OPENAI_API_KEY with exit 4, and a container POST reaches a mock upstream with the real key injected and the /v1 prefix preserved. * fix(verify-pr): harden the model proxy after adversarial audit Audit round 1 found the relay forwarded client hop-by-hop headers (connection, transfer-encoding, expect, proxy-authorization, ...) to the upstream, which RFC 7230 forbids and which fights undici's own framing for the relayed stream. Strip them, with a chunked-body test that pins the behaviour. Round 1 also found the CLI entry had no coverage: add tests for the missing-config exit code and the printed JSON coordinates (token override, base-path prefix, no real-key leak, health nonce), and document that the printed baseUrl uses the listen address, so binding 0.0.0.0 yields a URL clients cannot dial. Rounds 2 and 3 (open-ended re-read plus adversarial passes over the updated diff) found nothing further. All verify-pr suites: 123 passed. * fix(verify-pr): address review findings in the model proxy and checker Round-1 review (12 inline findings at |
||
|
|
276a447ef0 |
fix(ci): interleave PR review endpoint cohorts and report final routes (#16151)
* feat(ci): support same-model endpoint splits in PR review canary The endpoint override derived the cohort by comparing the routed model name against the configured canary/default models, so configuring the SAME model on both cohorts — a pure quota split across two gateways — labelled every PR 'default' and silently disabled the override. Derive the cohort from the bucket predicate itself instead (review_cohort_for_bucket: same clamp, same PR-number bucket test as the model router), so model name equality no longer matters. The notice's cohort field remains the attribution when both cohorts run one model. * fix(ci): interleave review endpoint buckets and report final routes Co-authored-by: Qwen-Coder <[email protected]> * style(ci): apply Prettier to the review endpoint bucket test The Lint & Static lane runs `node scripts/lint.js --prettier` on the files a PR touches, and this PR's own test file was not formatted: three `expect(...).toBe(...)` lines exceed the print width, so the incremental lint step exited 1 on every head. Formatting only - no assertion or fixture changed. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzj4h0j9a * fix(ci): share the endpoint host derivation and pin the cohort notice - Extract openai_base_host() so the proxy bypass and the cohort notice derive the host from one place; the notice cannot name a host the traffic was not routed through. - Drop the comments that still describe the removed model router. - Pin the notice's bucket count (8% -> bucket 87/100), make the noKey/badUrl rows exact strings, and assert initial_openai_host so the three mutations that survived this suite now fail. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvznesli9g --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
5b7f9ec1c3 |
fix(ci): repair desktop RPM dependency and teardown checks (#16154)
* fix(ci): repair desktop rpm dependency and teardown checks Co-authored-by: Qwen-Coder <[email protected]> * test(desktop): use a counted space class in the EPEL checksum pin `scripts/lint.js --eslint` runs on every file the PR touches, and the new rpm dependency test spelled the two spaces in the pinned `echo` line as literals: `no-regex-spaces` rejects them, so Lint & Static failed on the incremental ESLint step. `[0-9a-f]{64} {2}` matches the same two spaces the workflow emits; the assertion still pins exactly what the release step writes. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzj4h0j9a * fix(desktop): keep the runtime group as a teardown target Teardown collected daemon pids through the zombie-aware liveness predicate and escalated SIGKILL only on leaders that were still alive, so a leader that had already exited — reaped, replaced, or an unreaped zombie — silently dropped its process group, which is teardown's only handle on it. The children the leader spawned (MCP servers, tool subprocesses) then survived and kept writing under the workspace about to be deleted, and the smoke reported a pass. Collect every recorded pid, escalate on every collected group after the drain, and keep the zombie-aware predicate on the "survived teardown" report so the false positive this PR exists to fix stays fixed. The new fixture drives a runtime whose leader exits while a writer in its group ignores SIGTERM. Also pins the EPEL download URL and digest literals, drops the duplicate local liveness predicate in favour of the shared helper, and covers the signal-probe error arm (ESRCH gone; EPERM/EACCES alive, the rule the core helper documents). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzpjyde9j * fix(desktop): decide teardown on the runtime group, not its leader The round-2 escalation signals the recorded pid's group (`kill(-pid)`), but the verdict and the drain still asked `isProcessAlive(pid)`, which answers "gone" as soon as the leader exits — the exact topology `SMOKE_FIXTURE_EXIT_AFTER_SPAWN` reproduces. In that state `waitForExit` returned on its first poll, so the 5 s drain was skipped and the `survived teardown` branch was unreachable. `isTeardownTargetAlive` probes the group first and falls back to the leader where there is no group to signal (win32), so the drain and the leak verdict cover the children the group can still hold. Also drops the "mirrors the canonical … keep both in sync" claim from the desktop probe's comment: it diverges from core's `isPidAlive` on the pid <= 0 guard, the zombie exclusion on the EPERM/EACCES arm, and the vanished-/proc answer. The comment now states those differences and why teardown wants the dead-on-ENOENT direction. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzrp44z9n * fix(desktop): stop counting unreaped zombies as a live teardown group `waitForExit` asks `isTeardownTargetAlive` whether a daemon's group still exists. `kill(-pid, 0)` also answers for members that can no longer write, so a daemon that exited and that the container's PID 1 has not reaped kept its group "alive": the drain burned its five-second window, the second probe still returned the pid, and teardown reported "the packaged runtime survived teardown" for a runtime that was already gone. On Linux the group probe now counts only members that can still write: scan `/proc` for entries whose pgrp is the recorded pid, and report the group stopped when every member is a zombie. A scan that observes no member keeps the group probe's answer, so a hidden /proc cannot end a drain early, and the group handle still wins over the leader handle. Measured on a real zombie group (setsid + _exit, parent never reaps): /proc state Z, `kill(-pgid, 0)` exit 0, `isProcessAlive` false, and `isTeardownTargetAlive` true before this change, false after it. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzy4lf49w --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
90eb13e4c6 |
refactor(managed-agent): Make runtime backends optional Maven modules (#16141)
* refactor(managed-agent): Split runtime backends into optional modules Extract five backend implementations behind the shared provider SPI and support selected-backend executable distributions. Preserve SQL ownership, defer unavailable historical work, and make Spring own SSH connection cleanup. Co-authored-by: Qwen-Coder <[email protected]> * refactor(managed-agent): Narrow backend split to Maven modules Keep optional backend dependencies and safe assembly while deferring selective standalone distributions. Restore default packaging and document the provider configuration. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4dce337ea2 |
feat(ci): let the PR review canary cohort carry its own endpoint (#16130)
* feat(ci): let the PR review canary cohort carry its own endpoint The review canary split (QWEN_PR_REVIEW_MODEL_CANARY/PERCENT) only moves the model name; both cohorts share REVIEW_OPENAI_BASE_URL and its key, so a rate-limited canary model also burns the shared gateway's quota — the shape behind the 429-storm review timeouts. Add an optional endpoint pair for the canary cohort: QWEN_PR_REVIEW_BASE_URL_CANARY (variable) + REVIEW_OPENAI_API_KEY_CANARY (secret). When the bucket routes to the canary and both are set, base URL and key move with the model and the canary host joins NO_PROXY. A URL without its own key, or an unparseable URL, degrades to the default cohort with a warning — forwarding the default key to another provider would leak it. A canary model identical to the default model disables the override (buckets are indistinguishable). The cohort notice now prints the resolved cohort so rollouts stay attributable. Unset, behavior is byte-for-byte today's: model-only split on the shared endpoint. * fix(ci): drop inert canary NO_PROXY append and record the workflow's new size The canary endpoint override appended its host to NO_PROXY, but nothing in this job consumes it: configure_qwen_network unsets HTTPS_PROXY/https_proxy/ HTTP_PROXY/http_proxy before the override runs, qwen does not consult NO_PROXY when building its proxy agent, and the gh/git wrappers that do get the proxy back only talk to github.com. Removing the two exports also removes the comment that claimed a proxy-bypass symmetry the step cannot produce. Lint & Static was red in "Check workflow file size": 276955 bytes against a recorded 272192 with a 4096 allowance. The growth is the feature itself (the env pair, the override block, the resolved-cohort field), not prose that could move into a sibling file, so the baseline moves with the file rather than the rationale being cut to fit. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvz69ico8s * refactor(ci): simplify canary endpoint validation Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: qwen-code patrol <[email protected]> |
||
|
|
14de8a035e |
feat(verify-pr): inherit --publish consent on same-session re-verification (#16112)
* feat(verify-pr): inherit --publish consent on same-session re-verification A maintainer who ran `/verify-pr <n> --publish` and then typed the "有更新了,重新验证" follow-up trigger had to confirm publishing again: the skill inherited the previous round's coordinates but deliberately excluded publish consent, so every re-verification of an already published PR repeated a confirmation the maintainer had just given. Let a follow-up round inherit publish consent from the most recent round for the same PR in the same session. The round must announce the inheritance in one line before posting, `--no-publish` on the trigger opts out and breaks the chain, and the most-recent-round semantics keep a quiet round from reviving an older consent. Cross-session rounds still require an explicit request: the phrase alone posts nothing, and a published comment found on the PR is evidence of a past round, not consent for a new one. The CI lane and watch mode are unchanged. Update the two pin tests that fixed the old per-invocation-only rule and pin the inheritance, announcement, opt-out, and cross-session boundaries. * fix(verify-pr): carry inherited publish consent through follow-up chains Review of the first round found the consent chain broke after a single inheritance: R1 invoked with --publish, R2 a bare trigger that inherited and published, R3 a bare trigger whose most recent round (R2) was not itself invoked with --publish — so R3 fell back to asking again, the exact friction this change exists to remove. Model consent as a per-PR state of the session instead of keying it to the latest invocation: a --publish invocation grants it, a fresh /verify-pr round without --publish or a trigger carrying --no-publish withdraws it, and a bare trigger keeps it unchanged, so consecutive bare triggers publish under the original grant until a round withdraws it. Watch rounds keep their own standing consent and never change this state. Also close the two review suggestions: the Hard rules bullet now recognizes a request inherited within the same session, and the argument hint lists --no-publish so the opt-out is discoverable. Pins cover the grant/withdraw/keep mechanics, the three-round chain, the watch boundary, the Hard rules clause, and the updated hint. * fix(verify-pr): grant session consent on any explicit publish request Second review round found two gaps. The design docs for the assets coordinates still stated that publishing consent is per invocation, which reads as an invariant and could silently revert the feature; both language versions now defer to the skill's session-consent rule while keeping "coordinates are not consent". And the grant keyed on the literal --publish flag, while the publish rule also accepts an explicit request in words — a words-request round published but seeded no consent, splitting the friction this PR removes by request form. The grant now rides on the explicit request, flag or words alike, and the withdrawal matches it. Also close the deep verification's coverage survivors: the withdrawal is pinned as one contiguous sentence (a fresh round keeping consent now fails), the chain's upper bound, the announcement's naming duty, and the evidence-of-a-past-round clause each have pins, and an inherited publish now carries its one-line announcement into the comment body immediately before the attribution footer, so a PR reader can tell it from a per-round request. |
||
|
|
f643e579c5 |
fix(managed-agent): Restore profile-owned runtime verification (#16134)
Keep the shared Failsafe configuration in pluginManagement so ordinary release verification does not activate environment-specific integration tests. Preserve the dedicated profiles and executable artifact assertions. Backport the same POM repair to the pinned 0.26.5 source with checked patch digests, and verify both patches before applying either one. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
38b71299c8 |
feat(desktop): build and publish rpm installers (#16060)
* feat(desktop): build and publish rpm installers
Add Fedora 44 and RHEL 10 release roots, strict RPM smoke checks, and artifact naming that keeps each root distinct.
Co-authored-by: Qwen-Coder <[email protected]>
* fix(desktop): declare rpm runtime requirements and clear the yaml gate
The rpm legs built artifacts with no `Requires:` entries: Tauri's rpm bundler
writes them only from `bundle.linux.rpm.depends`, which nothing set, so
`verifyRpmDependencies` had no webkit soname and no glibc floor to find and the
release could not pass its own gate.
- Add `rpm-fedora44.conf.json` / `rpm-rhel10.conf.json` overlays declaring the
webkit soname and each root's glibc floor (2.43 vs 2.39 cannot share one
config), and pass the per-leg overlay to `tauri build --config`.
- Quote-free `container: ${{ matrix.container || null }}` stays as is with an
inline yamllint disable: quoting the expression would turn a null container
into an empty string. The shape test pinning that literal moves with it.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy1jgjo76
* ci(desktop): record the rpm legs in the workflow size baseline
`desktop-release.yml` is now 49858 bytes, past its recorded 45089 plus the
4096 allowance, so `Lint & Static` fails on the ratchet before any linter runs.
The growth is the two rpm legs: their matrix entries, the per-leg overlay
wiring, and the roots' comments. Moving that prose out would not pay for it —
the legs themselves are the bytes — so the number moves with the file in the
same PR, as the gate asks.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy1jgjo76
* style(desktop): apply prettier to the rpm legs and realign the workflow shape pin
Prettier flagged .github/workflows/desktop-release.yml and
packages/desktop/scripts/smoke-linux-installers.js on head
|
||
|
|
d9d6fc4a4b |
refactor(managed-agent): split Agent and Session stores (#16090)
* test(managed-agent): audit lock order across store collaborators Record the bilingual Store decomposition contract and entry ownership, transaction, lock and caller inventories. Follow final collaborator fields across source files while preserving owner-bound facade populations and existing ambiguity, transaction and compiled-member checks. Co-authored-by: Qwen-Coder <[email protected]> * refactor(managed-agent): split Agent and Session stores Move Agent and Journal responsibilities into immutable internal collaborators while retaining transaction facades and compatibility bridges. Resolve Journal dependencies at construction and migrate factories, fixtures, lock auditing and the bilingual implementation contract. Record focused verification and outstanding full-gate requirements separately. Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): repair store decomposition verification Keep declared field receivers visible across method-local type scopes while retaining fail-closed nested class handling. Preserve the Hosted CSI dependency identity assertion at its new owner. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |