mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
708ba347d355f199e9f5a6f4987755a3218bb5fa
50
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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]> |
||
|
|
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]> |
||
|
|
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]>
|
||
|
|
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. |
||
|
|
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]> |
||
|
|
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]> |
||
|
|
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]> |
||
|
|
ca7d487ce9 |
ci: route Lint & Static to ECS for every pull request (#15974)
* ci: route Lint & Static to ECS for every pull request * ci: isolate HOME and complete routing pins for lint_and_static - lint_and_static admits untrusted forks to the shared pool, so pin a job-level HOME/USERPROFILE under runner.temp and create it before the first write (git safe.directory, npm config). - Derive the classifier output from each job's own runs-on in the ceiling helper, so a timeout pointed at the wrong output throws instead of being substituted; parameterise runnerOn the same way. - Pin test's own runs-on to ubuntu_runner and add the HOME-isolation guard for every lane routed past the association gate. - Correct the qwen-autofix premise and the integration_no_ak comment, and record the routing decision in a bilingual design note. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvwdojb45 * fix(ci): isolate lint lane HOME and restore routing guard strength Resolve R1-1 (Critical): per-job HOME for lint_and_static so untrusted fork code cannot plant git/npm config a later trusted job inherits; shared warm caches re-pointed via getent + XDG_DATA_HOME/COREPACK_HOME. Resolve R1-2: timeout ceilings derive the output name from each job's own runs-on. Resolve R1-3: explicit ubuntu_runner pin for test. * docs(ci): record lint ECS fork-routing design and correct stale premises Resolve R1-5: bilingual design record (container tradeoff, acceptance, residual risks), qwen-autofix.md section 38 premise, no_ak scoping comment. * fix(ci): publish run-unique isolated HOME from step env, not job env Resolve P1/R1-1: jobs.<job_id>.env cannot read the runner context, so the workflow failed GitHub validation entirely; publish HOME/USERPROFILE via GITHUB_ENV from the first step instead. Resolve R2-1: RUNNER_TEMP survives across jobs on this pool, so the path is run-unique, cleared before create, and reclaimed by an always() step before the failure collector. Resolve R2-3: share content-verified linter/corepack caches via RUNNER_TOOL_CACHE. Resolve R2-5: move the HOME guard into the PR-gated routing battery with a single-lane set pin; resolve wenshao R2-4 with one fleet ladder for both outputs. Witness: actionlint 1.7.12 exits clean on ci.yml; six mutations each red the matching guard. * docs(ci): correct lint routing record per round-2 review Supersede link points at qwen-autofix.md section 38 (R2-4); read-only token and HOME-relative write class stated precisely (R2-2); residual risks are examples not a census, naming web_shell_e2e_smoke and other PAT pool jobs (wenshao R2-3); association gate recorded as throughput policy, not an enforceable boundary (R2-6); dispatch-over-fleet priority and per-lane hosted ceilings (P3); cache cost list and PR-gated test placement updated. * chore(ci): bump ci.yml size ratchet for merged tmpdir routing and HOME isolation Growth is real: #15979's helper-test tmpdir routing arrived via the main merge the freshness gate required, plus this PR's run-unique HOME mechanism. 156377 -> 161281 bytes. * fix(ci): keep COREPACK_HOME per-run; shared cache is a poisoning channel Corepack does not re-verify extracted entries on cache hit (reproduced locally on 0.36.0 with the repo's pnpm pin: tampered entry executes with the recorded hash unchanged, network disabled). Move COREPACK_HOME inside the run-unique HOME; XDG_CACHE_HOME stays shared because lint.js SHA-256-verifies linter archives on every hit. Guard and bilingual record updated; shared-corepack mutation reds the guard. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
cd4c917fcf |
fix(ci): route helper-test tmpdir off the host /tmp tmpfs (#15979)
* fix(ci): route helper-test tmpdir off the host /tmp tmpfs * test(ci): pin the helper-battery tmpdir routing against comments Two gaps in the #15979 change: - The pin used toContain/indexOf on the raw run block, so prefixing the export with `# ` disabled the routing while the guard stayed green. Filter comment lines before asserting, like the lint-history pin already does, and pin the private dir + EXIT trap the step now creates. - The step routed TMPDIR straight into the shared RUNNER_TEMP root. Copy the unit lanes' second half too: mktemp -d under RUNNER_TEMP and reap it on EXIT, so a cancelled battery does not leave its fixture tree behind (its own after() hook does not run on a signal). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvyiud548 --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
46d825d115 |
Revert "fix(ci): isolate web-shell smoke from shared runner networks (#15886)" (#15961)
This reverts commit
|
||
|
|
cc6de38cb5 |
fix(ci): isolate web-shell smoke from shared runner networks (#15886)
* fix(ci): isolate web-shell smoke from shared runner networks Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): clarify hosted smoke font guard behavior Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): explain dormant smoke pool steps Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): stop overstating which dormant smoke guards are pinned The retained comment claimed every self-hosted step's guard is pinned by checkout/preflight tests. Only two of six are: `Recreate runner Git metadata` (scripts/tests/qwen-runner-checkout-workflow.test.js) and `Use pre-installed Node.js (self-hosted)` (scripts/tests/no-ak-integration-ci.test.js). `Check host font baseline (self-hosted)` is named by no test file, and its `runner.environment == 'self-hosted'` guard can never be true under `runs-on: 'ubuntu-latest'` — so it is parked, not pinned. Say which two are pinned and that the font baseline guard is parked, and re-measure the ci.yml size ratchet entry (155625 -> 155708). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvugxtx22a --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
84aee5b9b7 |
test(web-shell): include transcript coverage in recovery fixture (#15883)
* test(web-shell): include transcript coverage in recovery fixture Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): complete recovery event fixtures Remove the unevaluated transcript type guard and supply required event terminal flags without changing assertions. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): use runner temp for Web Shell browser smoke (#15884) * fix(ci): use runner temp for Web Shell browser smoke Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): resolve runner temp in the browser step context Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): partition the complete macOS test suite into bounded jobs (#15888) * fix(ci): use runner temp for Web Shell browser smoke Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): resolve runner temp in the browser step context Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): partition the complete macOS test suite into bounded jobs Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): account for the macOS aggregate job budget Record the additional five-minute aggregate without changing any test execution timeout. Co-authored-by: Qwen-Coder <[email protected]> * test(install): isolate Windows fixtures from persistent PATH (#15889) Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): write the macOS shard matrix in block style and pin its gates The seven-row matrix used YAML flow mappings, which `.yamllint.yml` forbids (`braces: forbid: 'non-empty'`), so the `Run yamllint` step cannot go green on this branch. Rewrite the rows in the block style every other workflow matrix in this repo already uses. Add the test pins the shard plumbing was missing: - the `cli`/`core` dispatch argv, including the bare `--` that keeps `--shard` forwarded to vitest instead of consumed by npm, and the per-group `NODE_OPTIONS` choice - `id: 'install'` on `test_macos_shards`, which the suite's `steps.install.outcome` gate silently reads as '' if it is renamed - the aggregate `test_macos` gate against the shards' gate, so the preserved check name cannot report `skipped` while the legs fail Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvu6810z1y * fix(ci): preserve shell fixture gates on macOS system Bash Handle empty issue families under nounset without mapfile and explicitly reject either missing OSS credential. Keep failure assertions and include shell diagnostics. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): keep macOS aggregate failures eligible for infra retry Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
9acb3a5a1d |
fix(ci): pin PR retries to the triggering head SHA (#15734)
* feat(managed-agent): materialize inert external workspaces * feat(managed-agent): enable external files with verified close (#15685) * feat(managed-agent): add the external-workspace provisioning prerequisite (#15512) Step 3a of the external Hosted Workspace plan: a new `external-workspace` Runtime provisioner kind that places a session-isolated worker on a registered EXTERNAL storage tree and mints the boot document the shipped worker already admits (`managed-context/1` plus the existing `storage` member). The storage identity comes only from the durable registration: a read-only primary-key lookup that admits `REGISTERED` rows, bounds the revision to 2^53-1, joins the owner row on its own primary key, and fails closed when a persisted row disagrees with itself. A registration that cannot be read to completion stays retryable (`runtime_external_unavailable`) so the Broker keeps the placement adoptable instead of burying it. `external-workspace` accepts only the closed evidence set {storageRef, mountRoot, continuity}; `mountNonce` from the design is deferred and explicitly refused, because the registration record cannot persist it yet and an unverifiable claim must not be ignored. Hosted tools, EXECUTION admission, publication consumption, `stopDrained` and close gating are not enabled: `supportsDrainedStop()` stays false, every managed Workspace feature remains refused, and preflight now states the prerequisite in an `external-prerequisite` warning. No Session can be placed yet, and every placement fails closed. `external-scratch` behavior and wording, the shipped worker boot/storage/registration.json contract, the schema and the CLI are unchanged. * test(managed-agent): pin external storage refusal in lock canary * fix(managed-agent): require exact external workspace digest * fix(managed-agent): reject storage evidence for non-running workers * feat(managed-agent): add inert exact-worker external drain * feat(managed-agent): fence external workspace ownership and drain * feat(managed-agent): enable external files with verified close * docs: record verified external files and remaining phase two work * fix(web-shell): avoid recovery warning for closed sessions * test(ci): pin HostedExternalPublicFilesIT in the hosted-process class list #15685 adds HostedExternalPublicFilesIT, so hosted-process-ci.test.js's expected class list must carry it; the meta-test pins which IT classes the hosted-process lane runs and had 35 entries against 36 discovered. * fix(managed-agent): restore admission precedence and honour a LOST drain claim The external-files increment resolved the Workspace above the Agent pin, so a request whose stored definition cannot take effect, or whose revision cannot be selected, was refused on Workspace grounds first: it answered 404 workspace_required and a bare IllegalStateException instead of 409 agent_definition_unsupported / 400 unsupported_feature. Resolve the pin first again and keep the external pin rewrite once the Workspace is known. The close-claim validation admitted only DRAINING, but drainClaimedBinding deliberately keeps a LOST binding in LOST until its stop proof commits, so the arm refused its own claim with runtime_close_claim_pending instead of surfacing the stop's runtime_broker_recovery_blocked. BindingRenewal now records that its claim was taken on a LOST binding and admits that state. The settlement probe reads the storage kind to refuse an EXTERNAL tree the bridge holds no mount for, so its fixture needs the managed catalog rather than an empty database. The lock-order canary gains the new ExternalWork.apply/3 ambiguity, whose callee locks the Session row first and never reads ma_turn. * fix(ci): pin PR retries to the triggering head SHA * docs(ci): correct two comments the head-SHA pin made stale Pinning the four pull_request lanes to github.event.pull_request.head.sha left two comments describing the old checkout: the one above test_macos still cross-referenced the Ubuntu gate's superseded wording, and check-lint-gate-freshness.mjs still said the lint lane checks out refs/pull/N/head. Comment-only. ci.yml shrinks by 5 bytes (156090 -> 156085), so neither the .size-baseline ratchet nor the .ci-budget ledger moves. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvr2rokbyb * test(ci): pin all four PR checkout lanes to their whole ref expression The whole-expression `toBe` pin covered only `test` and `lint_and_static`. `web_shell_e2e_smoke` and `integration_no_ak` were guarded by `toContain(head.sha)` plus `not.toContain('refs/pull/')`, neither of which can see arm order: moving `|| github.ref` ahead of the pull-request arm keeps both green while the lane checks out the asynchronously rebuilt merge ref this PR exists to get away from, and the ancestor guard still passes because a newer merge tree contains the expected head. Pin both lanes against their own full ref literal, so arm order is part of the contract the same way it already is for the push-arm lanes. Verified one mutant at a time on each of the two lines: the new assertion turns red (1 failed | 79 passed) while the two substring assertions stay green, and the tree is byte-identical after reverting. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvr7204nyh --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e24f9c48fd |
fix(ci): deepen until the lint scope's own acceptance holds (#15644)
* fix(ci): deepen until the lint scope's own acceptance holds The deepen loop stopped as soon as `git merge-base --all` exited 0, but scripts/lint-scope.js also rejects an ambiguous merge base and a shallow boundary above it. A branch that merged the base tip proves a merge base immediately while its head-side boundary still sits above it, so the loop broke with zero deepens and lint-scope fell back to a full scan with three unused attempts. Mirror the consumer's acceptance instead: ambiguity stops the loop (deepening cannot fix it), a shallow boundary keeps it going. The two give-up paths — cap reached and an aborted deepen fetch — end in the same downstream warning whose remedy (a larger cap) is wrong for the aborted one. Emit the deepen count and the base depth reached so they are distinguishable in the log and the step summary. Pins now assert the mechanism rather than a bare `--deepen=` substring: the blobless deepen, the merge-base guard, the shared predicate call, `|| break`, the 3-round cap, and the give-up line. * fix(ci): report completed deepens and ambiguous history |
||
|
|
22c89204f0 |
fix(ci): deepen the incremental lint history until the merge base is provable (#15525)
A fixed 100-commit window expires in about a day at this repository's merge rate, after which merge-base finds no common commit and every longer-lived PR silently ran the full scan (measured when the finding was filed: 68.7% of recently-updated branches were more than 100 commits from their base, median 196). Keep the fetch bounded: after the depth-100 blobless fetch, deepen in capped steps until Git can prove the merge base. Each deepen transfers trees only, and the existing history-fallback warning still fires if the cap is reached. Verified with the literal step body under bash -e -o pipefail against a local filter-capable server whose fork sits 300 commits behind the base tip: the current single fetch leaves merge-base unprovable, the new loop proves it after one deepen round and yields the expected changed-file list. |
||
|
|
f885f39ee9 |
fix(ci): price ARM64 fleet lanes from the self-hosted label (#15529)
The two Linux lanes that raise their timeout on the shared self-hosted pool -- the merge-gating lint lane and the no-AK integration gate -- decided "reserved pool" by looking for the x64 fleet's name inside the classifier's runner label set. The ARM64 fleet renders ["self-hosted", "linux", "arm64"], which carries no fleet name, so the same contended machines were priced as hosted: lint at 45 minutes and no_ak at 30 instead of 90/60. An ARM run killed by the shorter ceiling reads as an ARM compatibility failure, which is the signal the fleet pilot exists to collect. Scope on the routing property that matters -- self-hosted versus GitHub-hosted -- and model the new key in the routing meta-test with an explicit ARM64 case. |
||
|
|
cc49daa282 | fix(ci): preserve independent validation for main push SHAs (#15508) | ||
|
|
10c036b655 |
fix(ci): keep the incremental lint history fetch blobless and visible (#15511)
Lint & Static can already hold a blobless merge base: Report test growth fetches it with --filter=blob:none. A later plain depth-100 fetch then fails resolving thin-pack deltas against blobs this checkout does not have (fatal: missing blob object + remote did not send all necessary objects), and continue-on-error turns that into a silent full scan: 4 of 8 sampled PR runs after #15381 merged, with 5 of 8 falling back to full lint in total. Fetch blobless too, and drop the recorded filter so later fetches in the checkout stay ordinary. lint-scope reads only commits and trees. Announce a history fallback as ::warning:: plus a step-summary line on workflow runs, deduplicated across the seven lint.js invocations, so the optimization can no longer stop working without a trace. |
||
|
|
1a98854b76 |
test(ci): pin the disk-pressure collector revision and FULL_PAYLOAD run bodies (#15475)
* test(ci): pin the disk-pressure collector revision and FULL_PAYLOAD run bodies Two deferred review findings from #10919 (issue #11032). Both are assertion-only: no workflow file is touched. The shared-prelude equality loop compares `test` and `lint_and_static` to each other, so both jobs' disk-pressure collector moving to a different `actions/upload-artifact` revision together leaves both serializations equal and reports green. The SHA-format regex on `Upload Test Results Artifact` is not a witness either: it accepts any 40-hex revision, on a different step. Pin the literal revision instead, resolved per job because the collector step carries the same name in both. The FULL_PAYLOAD test pinned name, order and gate, but only the two notices steps had a command body pinned: emptying `Run ESLint`'s `run:` or repointing it at `true` left every suite green while a required check stopped running the tool it is named for. Extend the existing loop with a name -> load-bearing-substring map that is total over FULL_PAYLOAD, and assert the substring rather than whitespace-exact text so reformatting one of the multi-line blocks stays benign. Verified by mutation in both directions. Against the pre-change suite, moving the collector pin in both jobs, emptying `Run ESLint` and repointing it at `true` all stayed green (44/44) -- that green was the defect. With these assertions the same three mutations fail, and the unmutated suite passes 45/45. Refs #11032 Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-issue-patrol/jmvmng32bjj * test(ci): pin collector retention, payload gates and failure propagation Addresses the four review findings on the pins this PR adds, all of them the same class of gap: an assertion that proves a string is present but not that the thing it guards still happens. R1-1: `with.retention-days` was unpinned on both disk-pressure collectors, so a coordinated edit reported green through the job-to-job equality loop (the loop is blind to both sides moving together). Pinned per job as the literal 14; the JUnit `Upload Test Results Artifact` step deliberately retains 7, so no shared constant. R1-2: map totality over FULL_PAYLOAD was only incidental — `toContain` coerces a missing entry to the literal string "undefined", which a body containing that word passes silently, and a stale key after a rename reads as coverage that no longer exists. Asserted the key sets match instead. R1-3: payload gates were filtered by a `ci_profile == 'full'` substring, so appending any further `&&` clause dropped a step from the unattended post-merge lane while every substring still matched. All 19 gates are byte-identical, so pin the whole literal. R1-4: containment proves the command is present, not that its failure reaches the job status. Pinned `continue-on-error` absent and no `|| true`/`|| :` in the body, plus `exit 1` in the settings-schema gate. Both stay inside the loop: `Report test growth` and `Fetch incremental lint history` set continue-on-error deliberately, and the helper-tests retry legitimately uses `|| {`. Each new assertion was shown to fail by mutating `.github/workflows/ci.yml` one edit at a time: the six gap mutants pass 45/45 against the previous head and each is killed by exactly the assertion added for it. The one-sided retention control still trips the pre-existing equality loop, so those greens were coordinated-edit blindness rather than a dead comparator. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvmwqg38tp --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
cdd5f1e027 |
ci: retain raw JUnit reports for same-repo runs (#15379)
* ci: retain raw JUnit reports for same-repo runs
* ci: keep the retained-report upload advisory and pin its guards
Review follow-up on
|
||
|
|
412c7c307d |
ci: start the web-shell smoke lane beside Test instead of behind it (#15408)
The lane has depended on the Test job since it was created, for exactly one value: the normalized CI profile. classify_pr produces that value itself (its classifier forces an unknown one to full), so the edge buys ordering rather than trust: Test takes ~34 minutes and the smoke lane ~35, and every full-profile PR pays that queueing twice in series. Gate on the classifier's output instead and both lanes start together. The '|| full' fallback keeps the old behaviour when the classifier emitted nothing, and the meta-test that evaluates this gate now models the term it reads. |
||
|
|
1f7ed6b50a |
perf(ci): scope PR lint and reuse static build outputs (#15381)
* perf(ci): scope PR lint and reuse static build outputs * fix(lint): keep incremental selection a superset of the full lane - Select .mts/.cts: flat-config --ext is additive to the config's own files scope, so lint:ci checks them; the selector dropped them and a PR touching only such a file reported green without opening it. - Stop escalating the whole-repository ShellCheck for terminal-bench paths the lane itself greps out. - Report every failing Prettier directory in one run, since Prettier runs per directory with basename targets and the first failure aborted the rest. - Pin RUNNER_TEMP/GITHUB_* in the subprocess fixture so its setup cannot rmSync the ambient runner linter temp directory. * fix(ci): fetch incremental lint history after the shallow refetches Both 'Check the API contract version advances' and 'Report test growth' refetch shallow history below the old position; a depth-1 fetch of the base ref grafts the commit LINT_BASE_SHA names, so merge-base fails and every contract-document PR silently ran the full scan. Pin the binding and the step order, correct the freshness header that still claimed the lanes run repo-wide, and record the per-file selection backstop. * chore(ci): record the ci.yml size ratchet bump Lint & Static failed on the workflow-size ratchet: ci.yml is 151690 bytes against a recorded 147136 with a 4096-byte allowance. The overage is this PR's opt-in step plus ci.yml growth that landed on main without a same-PR bump, which is the drift the ratchet exists to surface. The comment above the fetch step was cut to four lines with the mechanism moved into the design docs, and the recorded size now matches the file. |
||
|
|
2b05c8bd99 |
fix(ci): route web shell smoke to ARM64 independently (#15364)
* fix(ci): route web shell smoke to ARM64 independently * test(ci): pin the smoke lane's ARM64 routing in the meta-tests The runs-on change left two scripts/tests assertions pinning the old routing, so the Linux Test check and the nightly macOS/Windows lanes would go red once dependency installation succeeded (AGENTS.md guideline #3). - ci-platform-lanes: evaluate the real runs-on expression and pin self-hosted -> ARM64 plus the hosted and missing-output fallbacks, instead of the old equality with the shared test lane. - no-ak-integration-ci: replace the raw-YAML literal pin with a check that the lane still derives its runner from the classifier; the architecture override is pinned in ci-platform-lanes. - ci.yml: restore the #11250 pool history the routing change deleted, marked as x64-specific rather than a verdict on ARM64. * test: tolerate slow durable I/O in CI synchronization checks |
||
|
|
b6195e1bba |
fix(ci): run the trusted web-shell browser smoke on the ECS pool (#14471)
* fix(ci): run the trusted web-shell browser smoke on the ECS pool The web-shell E2E smoke is the second-largest user of the hosted ubuntu pool: on 2026-10-17 it took about 27% of hosted minutes, two thirds of that from same-repo PRs, while the hosted pool sat at its 20-job org cap and the ECS pool idled. #11250 moved the job hosted after four pool attempts failed in run 34081409590. Three of them ran the smoke with Playwright's unpinned default of half the host's cores, 8 workers against hosted's 2, and timed out broadly on visibility and clock waits across long-standing tests. The fourth's transcript gate took 73 s against its 60 s budget while npm install alone took 28 min on that host. Route the job like test's again, so trusted runs use the pool and untrusted fork PRs stay hosted, and pin the smoke at 2 workers, the hosted default, so a pool job no longer scales its browser count to the host. Drop the comments that described the pool steps as retained for a future reroute. * fix(ci): recreate runner Git metadata before the smoke checkout The smoke job now checks out on the reused ECS workspace, so it needs the same pre-checkout .git removal as test, lint_and_static, integration_cli and integration_no_ak. Otherwise actions/checkout reuses whatever the previous job left, including a broken shallow boundary or a live post-checkout hook. Hold the job to that rule in qwen-runner-checkout-workflow.test.js. * fix(ci): install WebKit's system libraries on pool hosts that lack them The first pool run of the web-shell smoke (run 39030597470, ecs-qwen-hk3-11) passed 272 of 286 tests in 10.6 min at 2 workers, and the transcript gate took 17.8 s against its 60 s budget. All 14 failures were mobile-webkit browser launches: the host lacked GTK 4, GStreamer, libwoff2 and about 40 more WebKit libraries, which Playwright's install step had already reported in its host validation. When that validation reports missing libraries, run Playwright's install-deps for Chromium and WebKit through sudo -n. An APT_CONFIG adds a dpkg lock timeout and the hosted step's network timeouts. The packages persist, so each host installs them once and later jobs skip apt. The install step gets the hosted step's 30-minute bound. |
||
|
|
20de6bf12e |
fix(ci): give the hosted Test lane the same 120-minute ceiling as ECS (#14474)
* fix(ci): give the hosted Test lane the same 120-minute ceiling as ECS The full unit suite no longer fits 60 minutes on GitHub-hosted runners. Those runners take fork PRs from untrusted authors and everything when the ECS kill-switch is on. On 2026-10-17, of the hosted full-profile Test runs that ended on their own, 13 passed in 45-59 minutes and 17 were cancelled at exactly 60 while still passing tests. Job 117375307261 is a typical run: install 7.4 min, cli 32.5 min, core 10.2 min, then cancelled six minutes into web-shell with the root scripts tests still to run. Each cancellation burns 60 hosted minutes and usually a manual rerun. Drop the per-pool split and use 120 on both pools. The test step's own 110-minute cap still bounds a genuine hang. * ci: record the Test lane's fixed 120-minute ceiling in the CI budget check-ci-budget sums only fixed timeout-minutes and counts an expression as a job without a fixed timeout. Turning the test job's per-pool expression into a fixed 120 therefore moves ci.yml from 180 minutes with five expression timeouts to 300 with four. The effective ECS ceiling is unchanged; the hosted one rises from 60 to 120 as intended. |
||
|
|
4d835faee5 |
fix(ci): document the shared-group supersede policy and keep main uncancellable (#14286)
* fix(ci): let main pushes supersede under the shared concurrency group A concurrency group holds at most one running plus one pending run, and a newer pending run replaces the older one even while cancel-in-progress reads false. Back-to-back merges to main therefore already dropped each other's run, only silently and one merge behind. Make the intent explicit: only the newest tree matters and it covers every merged change — the same stance e2e.yml already takes (#5859). Release branches stay uncancelled. * fix(ci): keep main uncancellable and document the pending-replacement policy Review on #14286 showed the flip cancels the in-flight main run: merges land every ~18min against this ~30min lane, no merge queue is enabled, so this run is the only fast witness of a merged tree, and its always()-gated coverage upload would publish partial reports under the complete artifact name. e2e.yml already measured that cancelling main starved the suite (67 of 100 push runs cancelled, 25 reported, #5859). Restore the main exclusion and instead make the existing behavior explicit: a newer pending run replaces the older one even with cancel-in-progress false, so back-to-back merges already coalesce onto the newest tree, and the in-flight run always finishes and reports — by design, not accident. |
||
|
|
70407feeb9 |
ci: run Java and Desktop checks daily or on demand (#14205)
* ci: run Java and Desktop checks weekly or on demand Move Java compatibility and integration checks out of PR and push events. Group Desktop Shell checks with the existing packaging dry run, replace daily packaging with weekly validation, and retain manual dispatch. Co-authored-by: Qwen-Coder <[email protected]> * ci: report weekly Java and Desktop failures as issues Include both weekly suites in the existing main failure analyzer and reuse its issue deduplication and autofix routing. Co-authored-by: Qwen-Coder <[email protected]> * ci: run Java and Desktop checks daily Co-authored-by: Qwen-Coder <[email protected]> * ci: format failure reporting workflow list Co-authored-by: Qwen-Coder <[email protected]> * ci: preserve lightweight guards after daily suite migration Co-authored-by: Qwen-Coder <[email protected]> * ci: align lint permission assertion and Java concurrency Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
df5470e430 |
feat(web-shell): Add workspace file browser and previews (#13878)
* feat(web-shell): Add workspace file browser and previews * fix(web-shell): Restore source preview CI fixtures and notices * fix(web-shell): Address file pane review blockers * fix(web-shell): Resolve review regressions and strengthen file checks --------- Co-authored-by: 钉萁 <[email protected]> |
||
|
|
c5b0e1f37f |
feat(managed-agent): Add offline W1b recovery bundles (#13138)
* feat(managed-agent): Add hosted file history and undo * fix(hosted): preserve file history fault gate contracts * fix(cli): settle hosted file history refusals safely * fix(hosted): recover file history baselines and settled turns * feat(managed-agent): Add offline workspace recovery bundles * fix(managed-agent): Preserve offline recovery source invariants * docs(managed-agent): Record latest W1b dependency integration * test(hosted): align reply-loss recovery assertions * fix(managed-agent): Preserve invalidated W1b recovery cuts * fix(cli): Fail startup on entry import errors * fix(managed-agent): Improve offline recovery diagnostics and connections * fix(ci): reserve hosted browser smoke time after installation * fix(recovery): preserve resumable bundle and lease evidence codex: address PR review feedback (#13138) * fix(recovery): avoid migration collision with Hosted Hooks codex: address PR integration feedback (#13138) Preserve main V27 and move the unmerged W1b recovery migration to V28 without changing SQL bytes. Synchronize bilingual evidence and unsupported Hook recovery boundaries. * fix(recovery): capture ordinary Hosted model resources codex: address PR review feedback (#13138) Include route and usage resources emitted by every Hosted model attempt, while preserving strict resource and settled-cut verification. Exercise the actual Session, Harness and activation producers in recovery regression tests and synchronize the bilingual recovery boundary and evidence notes. * fix(ci): keep managed failover ports distinct --------- Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: yiliang114 <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> |
||
|
|
3f56f74a6a |
feat(managed-agent): implement durable Hosted Hooks (H2) (#13129)
* feat(managed-agent): implement durable Hosted Hooks (H2)
* style(runtime-broker): remove blank line rejected by Checkstyle
* test(managed-hooks): align CI guards with H2 enablement
* fix(managed-agent): select current Turn by admission sequence
* fix(hooks): keep managed command environment out of argv
* fix(serve): close Hosted Hook recovery and capacity gaps
* fix(serve): settle cancelled startup hooks and identify deletions
* fix(hooks): settle cancelled callbacks and retry journal commits
* test(hosted): exhaust identical Store commit retries in fault gates
* fix(hooks): retain HTTP completion evidence after managed cancellation
* fix(hooks): settle cancellation before occurrence planning
* fix(hooks): settle proven-unstarted pre-tool cancellations
* fix(hooks): recover initialization and bounded cancellation results
* fix(hooks): settle cancelled initialization and bounded tool results
* fix(ci): bound hosted browser dependency installation
* fix(ci): record the ci.yml growth in the workflow size baseline
The hosted-browser install step adds two lines to ci.yml, taking it to
147136 bytes: 4110 over the recorded 143026 and 14 past the 4096-byte
allowance, which is why "Check workflow file size" failed. Most of that
growth is main-side drift the allowance had been absorbing, but this PR
touches the file, so the ratchet's stale-baseline leniency does not apply
and the number has to move in the same PR. The growth is real — a bounded
apt/Playwright install — so bump the entry instead of shrinking the
workflow.
Verified locally: .github/scripts/check-workflow-size.sh exits 0 against
base
|
||
|
|
ff5148e69e |
fix(ci): retry the helper-tests step once against pool contention (#12772) (#12780)
* fix(ci): retry the helper-tests step once against pool contention (#12772)
The Lint & Static lane failed on main at
|
||
|
|
a5a5eb556b |
ci(web-shell): give E2E Smoke 30 minutes (#12593)
* ci(web-shell): give E2E Smoke 30 minutes The job now runs 184 smoke tests after about 7.5 minutes of setup and ends right at its 20-minute limit: on #11206 three runs passed every test and were then cancelled during cleanup or the last test. Raising the limit stops passing runs from reporting red; sharding the suite is the fix at the source. * test(ci): pin the E2E Smoke limit at 30 minutes The lanes test records every job's limit with its reason; update the web-shell E2E Smoke entry to the new value and the runs behind it. --------- Co-authored-by: yiliang114 <[email protected]> |
||
|
|
ab526a9e87 |
test(ci): cover the review-scratch suite in the yaml import ratchet (#12259)
`review-scratch-cleanup.test.mjs` imports `yaml`, but it was missing from the `yamlSuites` list that pins the dependency-free fast lane off npm-package suites. The ratchet therefore could not catch that suite being moved into the lane that runs before any dependency install, which is the one failure mode it exists for. Add it, and correct the stale count in the comment above. |
||
|
|
7556821e65 |
fix(ci): retry transient E2E build artifact upload failures once (#11364) (#11375)
* fix(ci): retry transient E2E build artifact upload failures once (#11364) * test(ci): harden the e2e-build upload retry contract pins (#11364) Address review findings on the retry helper suite: scope the two-attempt assertions to the archive instead of every upload-artifact step and bind the attempts by step name, pin the pack-to-upload handoff (archive name and step order), pin the build job's absent job-level continue-on-error, pin the retry gate by whole expression so a failure() conjunct cannot silently disable it, compare the two with: blocks wholesale (minus the intentional overwrite asymmetry) plus the action pin, and compare downloaded names against every workflow upload instead of restating the four known legs. Also add the suite to the yamlSuites roster in ci-platform-lanes.test.js so it can never join the dependency-free fast lane undetected. * fix(ci): correct the upload-retry rationale and name-key its contract (#11364) Real-runner probes on the PR showed the comment's two reasons for the retry's overwrite do not reproduce: a stalled upload aborts before finalize and reserves nothing, and name uniqueness is scoped per run attempt so a manual re-run does not 409 either. Reword both comments to the window overwrite actually covers — a first attempt that finalized e2e-build and only then reported failure. The contract test picked "the archive" by position (uploads[0]), contradicting its own comment: an unrelated upload step placed before the archive upload reddened the suite with misleading messages. Key on the literal e2e-build name instead — already the pinned contract in scripts/tests/e2e-workflow.test.js — in both the filter and the download-leg assertion. Mutation probes: a decoy upload step before the archive now stays green, while decoy+retry-deleted, retry-deleted, retry-renamed, and overwrite-dropped all still fail. * test(ci): pin the pack-to-upload directory in the e2e retry contract (#11364) Co-authored-by: Qwen-Coder <[email protected]> * test(ci): pin the exact upload path in the e2e retry contract (#11364) Round 3's handoff pin closed the pack side but left the upload side pinned to a directory prefix, so moving only the two upload with.path values into a subdirectory of runner.temp kept the suite green while the runtime upload found no file and redded the build job. Compare first.with.path to the exact '${{ runner.temp }}/' + archive literal, reusing the derived archive binding rather than restating the name. Mutation probes: the upload-side drift now fails on the new assertion (16/1, expected-vs-actual), the workspace-escape and pack-side drift mutations stay red on their existing assertions, and the same upload-side mutation under the old prefix assertion stays green — the exact pin is the load-bearing one. Also reword the overwrite rationale in both the workflow comment and the test comment to the form the shipped action source supports: the record is created before any bytes move, so a failed first attempt can leave the e2e-build name taken, and overwrite is delete-then-upload and a no-op when nothing exists. The previous wording asserted a stall reserves nothing, which the create-then-upload lifecycle contradicts. And name the retry contract's actual ungated siblings (run-e2e-tests.sh's sandbox-image build retry, build-and-publish-image.yml's image build retry) instead of the budget-gated sandbox:none shard retry the comment over-claimed parity with. * fix(ci): pin the unpack archive name and restore the overwrite rationale (#11364) Round 4's handoff pin derives the archive name from the upload with.path, so renaming the tarball at the pack step and both upload paths while the four legs' unpack arguments stay put kept the suite green while every leg would fail closed on main (review R1-3). Pin each 'Unpack build artifact' run's trailing argument against the derived name; the legs download into runner.temp/e2e-build/, so the comparison is a suffix match, not the upload's full path. Mutation probe: a producer-only rename now fails the contract with 'a leg unpacks a different archive than the build job uploads' (16 pass / 1 fail), the unmutated workflow stays green (17/17), and the same mutation was green before this pin (17/17). Also restore the overwrite rationale that |
||
|
|
70cf363395 |
feat(web-shell): Improve split-view session navigation (#11250)
* feat(web-shell): Improve split-view session navigation * fix(ci): Run Web Shell browser checks on hosted runners * fix(web-shell): Keep split navigation outside approval shortcuts * fix(web-shell): Address split-view review regressions * fix(web-shell): Stabilize split approval reports * fix(web-shell): Preserve history anchors during slow rendering --------- Co-authored-by: 易良 <[email protected]> |
||
|
|
f5ce3ea8fd |
fix(ci): give web-shell E2E Smoke the ECS timeout allowance its siblings have (#11212)
Four jobs route to the self-hosted pool through the same `ubuntu_runner` expression. Three of them scale `timeout-minutes` for `ecs-qwen` — test 120/60, lint_and_static 90/45, integration_no_ak 60/30 — and `lint_and_static` carries a comment explaining why: the pool's contention, with hosted runners kept on the tighter ceiling so a genuine hang there does not burn the ECS allowance. `web_shell_e2e_smoke` was the fourth, and the only one still priced flat at 20. It lands on the same hosts under the same contention and got none of that allowance, so it was beheaded twice on a single commit that changes nothing it tests: on hk3-9 `npm ci` alone ran 19m46s of the 20 and never finished, and on hk4-23 install took 10m33s against 4m53s on hk5-2, leaving the browser smoke 8m15s before the job died against 2m29s warm. Both phases running 2-3x is the pool rather than the dependencies. A cold npm cache would explain a slow install but cannot explain the smoke phase, which is a vite dev server plus a headless browser and touches no cache at all. The healthy path is 8-10 minutes, so a flat 20 only ever tolerated a 2x slowdown — exactly what the contended hosts deliver, and the per-host medians already measured in #10879 (hk3 34.6 min, hk4 27.5, hk5 12.3) say which attempts were going to die before they started. 40 gives the ECS path the same 2x its siblings use and covers the measured worst case with room for a 3x; hosted keeps 20 for the reason lint's copy gives. The lane was not covered by the guard that exists for exactly this regression. `ci-platform-lanes.test.js` already evaluates the real timeout expression for both routings so that, in its own words, "a regression to an unconditional ceiling fails here instead of reading as a passing constant" — but it pinned only the three siblings. Add this one on both routings. Three mutants applied, all killed by exactly the intended test: reverting to the flat 20, reducing the ECS allowance to 30 (under the measured worst case), and raising it to a flat 120, which the hosted-ceiling test also catches. |
||
|
|
703678136a |
perf(cli): import core modules directly instead of the package root (#10957)
* perf(cli): let tests resolve core modules individually, and stop two files importing the whole package
Importing from the core package root pulls in its entire export graph — a bit
over six hundred modules — however little of it a file actually uses. In a
release run the cli workspace spent 2223s collecting modules against 1372s
running tests, and a file that imports the package root costs about 11.5s
before its first assertion where one importing a single module costs about 2s.
cli's tsconfig already maps a wildcard subpath onto core's sources, so esbuild
resolves per-module imports when it bundles. Vitest does not read tsconfig
paths, and the alias list that stands in for them named only four subpaths, so
those imports did not resolve under test at all. This adds the wildcard there.
Expressing the alias list as an ordered array is what allows a pattern entry.
The package root has to become an exact match in the process: as a string it
would also match everything beneath it and rewrite each subpath into a path
under index.ts.
Two files move to per-module imports as a first check that the mapping holds
end to end. Both were picked because nothing that depends on them replaces the
core package with a mock factory — where a test does that, the mock stops
intercepting once the code under test imports the module directly, so those
call sites need their mocks moved in the same change and are left alone here.
* fix(cli): restore the named core subpaths the previous commit dropped
The previous commit was assembled from a working copy that predated main by
several weeks, so it silently reverted this file to that older state. Four
named core subpaths added since — envVarResolver, noFollowOpen,
subSessionConstants and toolWriteOrigin — disappeared with it, and the new
wildcard then claimed those specifiers and pointed them at files that do not
exist. 257 test files failed to load as a result.
All eight named subpaths are restored and kept ahead of the wildcard, with a
comment saying why that order matters and what a contributor adding a ninth
has to do. None of the eight can be derived from its specifier, so none of
them can be folded into the pattern.
The two migrated source files are rebuilt on their current contents for the
same reason; one of them had also been reverted by a line.
* perf(cli): import core modules directly where no test mocks the package
Importing from the core package root evaluates its whole export graph — a bit
over six hundred modules — however little of it a file uses. On the release
lane the cli workspace spends more time collecting modules than running tests,
and on the main lane it now takes 84 minutes on its own, most of it collection.
These 130 files ask for named modules instead. They were chosen by checking,
for every test whose module graph reaches them, whether that test replaces the
core package with a mock: a test that swaps the package wholesale stops
intercepting once the code under test imports a module directly, and a test
that spreads the real package and overrides a few names only matters if one of
those names is what the file imports. Files with either kind of coupling are
left for a later change that moves the mocks at the same time.
Only import statements move; every other line is byte-identical.
* fix(cli): point ProviderModelConfig at the module that declares it, and wrap long imports
Two problems with the previous commit, both found by CI.
The symbol map resolved a re-exported name to the module that re-exports it
rather than the one that declares it, so `ProviderModelConfig` was asked of
`models/types` when it is declared in `providers/types`, and the build failed
to typecheck. A checker now confirms, for every generated specifier, that the
named module really does export that symbol — following its own re-exports —
and it reports one bad pair out of 492.
The formatting pass that was supposed to run over these files had silently
done nothing: invoked from the repository root against paths outside it,
Prettier skips the files and still reports success, so long import statements
went out unwrapped. Rerunning it properly reflows 47 files.
Two files are left with an over-long line Prettier would wrap, because that
line is over-long on the base commit too and the lint gate does not flag it;
reformatting it here would be unrelated noise. Every other line outside an
import statement stays byte-identical.
* perf(cli): import core modules directly in another 114 files
The same mechanical change as the previous commit, over the files a corrected
reading of the test suite showed were always safe to move.
The earlier pass classified a test as replacing the core package if the text
of such a call appeared anywhere in it, including inside a comment. One file
only mentions the pattern in a doc comment explaining why it deliberately
avoids it, and being counted as a blocker there ruled out 217 modules that
nothing actually blocks. Ignoring comments when detecting the call raises the
number of files movable without touching a single test from 141 to 260.
Every generated specifier is checked against the exports its named module
really has, following that module's own re-exports — 865 pairs here, none
wrong. Outside import statements every line is byte-identical.
* test(cli): move three barrel mocks onto the modules they actually stub
Where a test replaces the whole core package with a factory, the code under
test cannot move to per-module imports on its own: the mock would stop
intercepting and the real implementation would load instead, quietly changing
what the test exercises while leaving it green. The mock has to move in the
same commit.
These three are the cases where that is unambiguous — every name the factory
stubs is declared in one module, and the code under test imports exactly those
names. Each pair moves together onto that module.
The pattern generalises: about sixty tests each hold back one or two modules
this way, and roughly ninety more modules are held by several tests at once
and need them changed together. Establishing the shape on the clean cases
first keeps the ambiguous ones honest.
* Revert the migrated modules that any failing suite depends on
Retargeting this PR at main got the unit suite to run for the first time, and
it failed: 16 files, 127 tests, none of them failing on the jsdom PR that
shares the same base. So they are this stack's doing.
Every one is the same shape — a stub the suite installs no longer intercepts
once the code under test names a module instead of the package root. The
static analysis that picked these files models three ways of installing such a
stub and misses at least two more: a spy planted on a namespace import of the
package, and whatever six of the sixteen suites do, which it cannot parse at
all. Sharpening the heuristic further is not the answer; it was already wrong
in a way no amount of local reading would have caught.
So this restores every migrated module that any failing suite reaches, 146 of
them, and keeps the 110 that nothing failing depends on. That is blunt — some
of the 146 are certainly fine — but it is the version that can be shown to
pass, and picking the survivors apart is work for a run that is green to begin
with.
* fix(core): let consumers outside the cli package resolve a core module by path
The integration gate failed to compile against the migrated files:
error TS2307: Cannot find module '@qwen-code/qwen-code-core/utils/debugLogger.js'
error TS2307: Cannot find module '@qwen-code/qwen-code-core/utils/editor.js'
Only packages/cli maps these specifiers, through a wildcard in its own
tsconfig. The integration suite lists the eight named subpaths and no
wildcard, and the package's exports map has entries for those same eight plus
the dist and src trees — so anything resolving the normal way, this suite and
any consumer of the published package alike, cannot name a core module.
Both gaps close here: the wildcard is added to the integration suite's path
mappings, and a catch-all maps a bare module path onto the build output. The
catch-all exposes nothing new; `./dist/*` already reaches the same files.
This is the part of the change with consequences beyond the test run. The
shipped CLI is a single bundle and never resolves these specifiers at runtime,
but the package is published, and until now a migrated import was only
resolvable from inside the one workspace that happens to map it.
* fix(cli): repair the mock pairs the revert split, and keep dev on source
Three findings from review, all of them consequences of earlier steps here.
Two mock pairs were left half-migrated. In one, the mock and the code under
test both moved to the module, but the suite's own import of the same two
functions still read the package root — so the suite held the real functions
while the code held the stubs. In the other, the revert restored the code to
the package root and left the mock pointing at a module nothing imports any
more, which stubs nothing at all. The first is completed, the second put back.
The exports catch-all also changed what `npm run dev` runs. Its loader
intercepts the exact package root and nothing else, so subpath imports fall
through — and where they used to fail to resolve, they now quietly reach
compiled output from whenever the tree was last built. The loader now redirects
them to the source tree when the source file is there, leaving the named
subpath exports, whose file names do not mirror their specifiers, to resolve
as before.
* Cover the exports catch-all, and stop it hiding from the architecture rule
Three review suggestions, all about the `./*` entry this branch added to core's
exports and the things that quietly depend on it.
Nothing exercised that entry. In the repo, cli's subpath imports resolve
through tsconfig paths or vitest aliases; in the published package neither
exists and Node resolves them against `exports`. So removing the entry, or
renaming the dist root, would leave every suite green and break `qwen` on its
first core subpath import. A new check runs Node's own resolver against the
built package — deleting the entry turns it red, which is the property that
makes it worth having.
The entry also widened what the utils-layer rule has to police. It resolved
self-references by exact key only, so a deep specifier like
`.../config/storage.js` matched nothing, returned early, and reported nothing —
while resolving perfectly at runtime. It now follows Node's own order: exact
keys first, then the longest literal prefix among patterns. Fixtures cover the
wildcard idiom, exact-key precedence, and a sibling utils import that must
still be allowed.
Finally the integration suite's path mappings. The guidance comment above them
said a bare wildcard falls through to dist, which stopped being true when the
wildcard was added — nodenext substitutes `.js` for `.ts`, and the program
resolves core subpaths through it to source today. Left alone, a maintainer
following that comment would delete the wildcard as a violation and silently
re-route live imports to dist declarations. The comment now describes what the
block does, and says why the six alias-style entries cannot be folded into the
pattern. Two entries that had drifted out of sync with core's exports map,
toolWriteOrigin and envVarResolver, are restored.
* fix(cli): point the session-picker branch mock at the module it imports
Review finding R1-4b. StandaloneSessionPicker now takes getGitBranch from
@qwen-code/qwen-code-core/utils/gitUtils.js, but the colocated suite kept
mocking the package root, so the override no longer intercepted anything the
picker resolves and the factory's importActual still pulled core's whole
index into the suite's module graph. The mock now names the same specifier
the component imports, and a small pin test goes red if the pair splits
again.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtme4yqxhl
* fix(dev): preserve source-backed core entry paths
Co-authored-by: Qwen-Coder <[email protected]>
* fix(scripts): close core subpath verification gaps
Co-authored-by: Qwen-Coder <[email protected]>
* fix(scripts): gate dep-only core subpath exports in the check
The exports check collected specifiers only from packages/cli/src, but
acp-bridge and sdk-typescript are runtime dependencies of the cli whose
compiled dist keeps core subpath specifiers verbatim. Entries named only
by those packages (./goalWire, ./transcriptRecords, ./subSessionConstants)
were therefore ungated: deleting one kept every gate green while
`node packages/cli` died with ERR_MODULE_NOT_FOUND on the serve/replay
path. Extend the source scan to packages/acp-bridge/src and
packages/sdk-typescript/src so all 95 collected specifiers resolve
through the exports map. Verified: removing ./goalWire or
./transcriptRecords from packages/core/package.json now makes the check
exit 1 (entries restored after the mutation probe).
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtmxfessij
* docs(harness): note terminal-capture loader is manually exercised
No CI test executes the terminal-capture harness loader's subpath
redirect; it runs manually only via `npm run test:terminal-bench`.
Record that at the redirect site and point at
scripts/check-core-subpath-exports.mjs as the CI gate for the real
resolution path, so the `named` map stays consciously in sync with the
named entries in packages/core/package.json.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtmxfessij
* fix(scripts): reject core subpath targets outside the published dist
The exports check validated resolved targets against the repo tree, but
core's exports map also carries "./src/*": "./src/*" while package.json
publishes only dist, vendor and scripts/postinstall.js. A specifier
routed through that entry resolved to a real in-repo packages/core/src
file, passed the bare existsSync and exited 0 even though the published
artifact ships nothing — the installed CLI would die at startup with
ERR_MODULE_NOT_FOUND while the gate stayed green. Require the resolved
target to lie inside packages/core/dist/, where every legitimate
runtime specifier lands.
Add scripts/tests/check-core-subpath-exports.test.js: a fixture-tree
suite (per the scripts/tests convention) that runs a copy of the real
script in a temp workspace. It pins the dep-only scan from
|
||
|
|
133a1a39de |
ci: skip product tests for ECS updater maintenance (#10548)
* ci: classify ECS updater maintenance as CI-only Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): harden CI-only classifier trust Co-authored-by: Qwen-Coder <[email protected]> * ci: record ci.yml growth in the size baseline The ci-only-profile wiring added real workflow content; combined with prior main growth this pushed ci.yml 4853 bytes past its recorded 98043, over the 4096 allowance. Bump the ratchet to the current 102896 so the check reflects the reviewed growth. Co-authored-by: Qwen-Coder <[email protected]> * test(ci): gate the classifier trust test to the Linux lane The fail-closed trust test drives the workflow step through `bash -c` with a Unix ':'-joined PATH and an extensionless shebang `gh` stub, which the Windows lane cannot express. Gate it the same way this file's other bash-driven cases already do, keeping the Linux lane as the authoritative coverage (scripts/tests/vitest.config.ts). Co-authored-by: Qwen-Coder <[email protected]> * ci: log the reason behind the classifier trust verdict The permission lookup discarded all diagnostics (2>/dev/null || true) and the log printed only the verdict, so an API outage, an empty permission, and a real below-write permission all produced the identical "PR classifier trusted: false" line. Record which branch made the decision and print it alongside the verdict; the can_trust_pr_classifier output and its fail-closed semantics are unchanged. Bumps the size baseline for the added lines. The trust test's helper now captures the step's stdout, and a new Linux-gated case asserts the denial reasons stay distinguishable. Co-authored-by: Qwen-Coder <[email protected]> * test(ci): pin the remaining classifier trust reason branches The R1-3 reason logging added six per-branch reason strings, but the 'not a pull_request event' and 'PR author missing from the event payload' branches were unreachable from the tests: runClassifierTrust hardcoded GITHUB_EVENT_NAME and PR_AUTHOR, so dropping or swapping either assignment shipped with every test green. Give the helper optional eventName / prAuthor overrides and extend the reason test to assert both branches stay denied and distinguishable; the merge queue exercises the first one on every merge_group run. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): run PR classifier from trusted base Compute the CI profile once from an immutable base-SHA checkout, then pass the validated output to product jobs without executing the PR head's classifier. All missing, failed, or unknown classifications continue to fall back to full CI. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): prepare trusted classifier checkout Clean shared-runner residue before checking out the trusted base classifier and update the workflow test fixture to mirror its isolated path. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): sweep leftover trusted-ci-classifier before checkout actions/checkout keeps an existing checkout directory when its origin URL matches and runs `git checkout --detach` inside it without a core.hooksPath mitigation, so a leftover trusted-ci-classifier from an earlier classify_pr run on the shared ecs-qwen pool would execute whatever hooks residue planted there — code execution through the classifier checkout the sweep already defends. Add the path to the pre-checkout sweep loop in every ci.yml copy (the byte-identity pin keeps them in lockstep) and in qwen-code-pr-review.yml, per the documented third-residue-name contract, and pin the removal behaviorally. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmthmo4kw80 * test(ci): pin the shared-pool ownership restore step's existence The ordering assertion alone survives removing 'Restore workspace ownership': restoreIdx becomes -1 and cleanIdx > -1 still holds, while root-owned leftovers from a containerised job defeat the sweep and the checkout again — the EACCES incident class the step exists for. The step is new in classify_pr on this branch, so pin it explicitly on every shared-pool job. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmthmo4kw80 * fix(ci): pin the profile producer id and log the degraded fallback Two silent degradations in the new profile wiring: - The classify_pr job output resolves against step id ci_profile, but nothing pinned the producer step's id or its GITHUB_OUTPUT write: a rename empties the output and every consumer silently runs the full profile. Pin both on the producer step. - When classify_pr fails (continue-on-error) the consumers coerce the empty output to full and log the exact same line as a legitimate full classification. Emit a ::warning breadcrumb when the producer output is empty, on both consumer copies, and pin it. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmthmo4kw80 * test(ci): pin the profile consumers' allowlist and output write The consumer loop pinned every load-bearing line of both `Use trusted CI profile` steps except the allowlist case line and the GITHUB_OUTPUT write. If a later edit drops the echo in either consumer, steps.ci_profile.outputs.ci_profile becomes empty and every downstream profile gate mis-compares. Extend the pinning test to cover both consumer copies of those two lines (mutation-probed on all four consumer lines). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmthzj3978p * docs(ci): explain the third residue name in the review sweep comment The sweep loop grew to three names (adding trusted-ci-classifier), but the comment above it still said "those two known names". Update the count and add the explanatory paragraph for the third name, mirroring the ci.yml copy: the residue comes from ci.yml's classify_pr base-SHA checkout on the shared pool, and actions/checkout would reuse it and run git (hooks included) inside it. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmthzj3978p * fix(ci): run only dependency-free helper tests in github_ci_only lane (#10548) The github_ci_only helper step runs before any dependency install — the setup-node and `npm ci` steps are gated on the full profile — but it ran the entire HELPER_TESTS list, 8 of whose suites import the `yaml` npm package. A PR classifying into this lane (e.g. a fork PR touching only .github/workflows/update-ecs-runner-qwen.yml, which this PR admits to the fast lane) failed closed with ERR_MODULE_NOT_FOUND on a fresh runner. Split the list: the fast lane now runs HELPER_TESTS_DEP_FREE (suites whose import closure is node: builtins only); HELPER_TESTS keeps the full list for the full-profile helper step, and the merge queue's full run re-checks what the fast lane skips. A pinning test in ci-platform-lanes keeps the yaml suites out of the fast lane. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmtire6cja9 * fix(ci): drop yaml-dependent helper suite from dep-free test lane web-shell-visuals-publish.test.mjs statically imports the `yaml` npm package, but HELPER_TESTS_DEP_FREE runs before any dependency install (npm ci is gated to the full profile), so the suite failed with ERR_MODULE_NOT_FOUND on fresh runners (review feedback from qqqys). Remove it from the dep-free lane only; the full-profile helper lane still runs it. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmtj6easob2 * fix(ci): keep yaml-dependent visuals suite out of github_ci_only lane web-shell-visuals-publish.test.mjs statically imports `yaml`, so the dependency-free github_ci_only lane can never execute it. Listing it in GITHUB_CI_ONLY_FILES meant a PR touching only that suite got every required check green without the changed file running once, because the full-profile helper step is gated on ci_profile == 'full'. Drop the entry so such PRs keep the full profile, and pin the classification with a test assertion (verified red with the entry, green without). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmtjj99h9bs * fix(ci): grant classify_pr pull-requests read scope and pin the fast-lane exclusion F1: classify_pr had no job-level permissions block, so its token carried only the workflow-level {checks: write, contents: read, statuses: write} grant. The classifier wrapper calls repos/{}/pulls/{}/files and repos/{}/pulls/{}, which 403 without pull-requests scope and fall back to full CI. Grant contents: read + pull-requests: read on the job and pin the block with an assertion in the no-ak integration test. F7: the fast-lane pinning test enumerated 8 of the 9 yaml-importing suites; add web-shell-visuals-publish.test.mjs (the suite the F6 commit excluded from HELPER_TESTS_DEP_FREE) and fix the comment count, so re-adding it to the fast lane fails the test. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmtjlef91bw * ci: give the github_ci_only fast lane a single dependency-free owner lint_and_static's 'GitHub CI helper checks' step ran the full HELPER_TESTS list under the github_ci_only profile, where every dependency install in that job is gated on ci_profile == 'full' - so the 9 suites that statically import `yaml` died with ERR_MODULE_NOT_FOUND on a fresh hosted runner. This branch newly routes ECS-updater and actionlint PRs into that profile, which made the fast lane deterministically red on exactly the flow it exists to serve. Repoint that step at HELPER_TESTS_DEP_FREE and drop the duplicate copy in the `test` job: ci-platform-lanes.test.js reserves that step name for lint_and_static, so the copy also broke the shared-prelude byte-identity guard and the out-of-test pin. Select the fast lane by its gate rather than by the list it consumes, so a regression back to env.HELPER_TESTS cannot drop out of the filter. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmto8kxx1l2 * ci: keep the ECS updater workflow guard reachable from its own fast lane Routing .github/workflows/update-ecs-runner-qwen.yml into GITHUB_CI_ONLY_FILES left its guard unreachable on exactly the PRs that change it. The 21-case vitest suite under scripts/tests/ only runs under the full profile — test:scripts is gated on ci_profile == 'full', and test_macos / test_windows fire only on merge_group|schedule|workflow_dispatch — while the dependency-free node:test replacement was a strict 7-case subset. Renaming the matrix job name template, which breaks the reporter script startswith(poolPrefix) filter, or dropping the issues:write permission from report_failure, merged with every check green. Port the remaining 14 cases into the dep-free suite behind the same win32/jq capability gate the bash replays already used, and delete the vitest copy so the two cannot drift. The suite imports node: builtins only, so it stays inside the HELPER_TESTS_DEP_FREE contract. Verified locally: 21/21 pass, 0 skipped; renaming the matrix job name template reddens "filters the run jobs by the prefix the matrix job actually uses", and dropping issues:write reddens "reports a failed fleet update only when a pool actually failed". Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmto8kxx1l2 * style: apply prettier to the ported ECS updater guard suite The ported assertions were hand-converted from vitest and drifted from the repo Prettier style, which the full-profile Run Prettier step would have flagged on this PR. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmto8kxx1l2 --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e3d26283e6 |
ci: close the guard holes round 2 found in the lint lane (#10919)
* ci: close the guard holes round 2 found in the lint lane
Nine follow-up findings on #10756's guards, merged before the round posted; all confirmed, fixed here.
The serializer at the heart of the prelude-identity guard was wrong: JSON.stringify's array replacer is a property allowlist applied at every nesting level, so each step's nested with:/env: serialized as {} and the guard could not see the fields it claimed to guard — Checkout's ref/fetch-depth, setup-node's versions, and the TRUSTED_CI_PROFILE binding whose drift to a literal would skip every profile-gated step while reporting green. Replaced with a recursive canonicalizer. The divergence exemption is now the field, not the step: a whole-step exemption left the collector's uses: revision — the one action pin in the job — compared by nothing, so a retag to a mutable @v4 was invisible.
Order is now pinned too: name-keyed maps are order-blind, and the steps context reads '' for steps that have not run yet, so a profile producer parked below its consumers would evaluate every ci_profile == 'full' gate false and complete the lane green having executed nothing. And the payload itself — the twenty moved steps the job exists to run — was pinned by nothing at all: deleting Run Prettier, flipping its gate, or re-adding a copy to test all stayed green. An exact ordered-list assertion now holds the full-gated payload, keeps it out of test, and pins the two deliberately-different gates as different.
runs-on joins the pinned surface (whole-expression, equal to test's): pick_runner is the fork-trust boundary and a literal pool label array would bypass it whole while desyncing from the timeout expression that reads the same output. no-ak's trusted-profile consumer loop gains the third consumer.
Two fixes to the collector work itself: it moved to the job's last step — failure() is evaluated when a step is reached and never revisited, so at its old slot just after install it had already been passed, unfailed, before any lint step could die, and could only ever collect a prelude failure. And a failure-gated disk-state dump now precedes it: the install sampler dies with that step's trap EXIT, so the artifact otherwise held only install-window samples for a death in any of the 19 later steps. The dump appends; a bare > would truncate the window it exists to complement.
The lane's banner no longer overstates its case: the property that justifies the required-check role is deterministic outcome, not lightness — the bundle-closure step builds and bundles the CLI, the helper suites spawn subprocesses, and the pool contention the old text denied is exactly what the routed 90/45 ceiling prices. It also records the honest limit: the incidents that motivated the split failed in the unit-test step that stayed in test, and this lane would have been green on those merges.
All ten mutations red: nested fetch-depth drift, TRUSTED_CI_PROFILE literal, producer below install, payload deletion / gate flip / leak-back, collector retag, runs-on literal, collector at its old slot, dump removed.
* fix(ci): keep the failure dump when the disk is full, and pin four guard holes
Review round 1 found five holes in the guards this PR added. Four are fixed
here; the fifth is a comment that misstated its own mirror.
The state-at-failure dump wrote its only copy into the samples file — the very
file whose writability the dump exists to investigate. Under ENOSPC the
redirect failed, `2>/dev/null` and `|| true` swallowed the failure and its
status, and the step exited 0 having recorded nothing, so oncall could not tell
"dumped, nothing interesting" from "could not write". Mirror through `tee -a`,
which still emits the data to stdout when the file write fails and reports why
on a stderr the block does not swallow. An executed case occupies the target
path with a directory and asserts the dump survives in stdout while the step
stays green.
The other three are pins that did not bite:
- The shared-prelude floor stayed at 12 after the divergence exemption narrowed
and re-admitted the collector, making the intersection 13. A floor below the
real count lets any one shared step be deleted out of either job unnoticed.
- The serializer at the heart of this PR had no fixture. Both sides of every
comparison flow through it, so regressing the call site to the pre-PR array
replacer compares {} to {} and the drift loop reports green — the exact blind
spot the recursion replaced. Pin a known nested key on the serialized OUTPUT
rather than on the serializer alone, because the regression that matters is a
call site swapped back to an allowlist.
- Nothing pinned the `needs` edge that makes every classifier output this suite
reads non-empty at runtime; deleting it decouples the lane silently, dropping
runs-on to hosted and taking the degraded empty→full profile fallback.
And the collector's comment cited `test` as its mirror for lastness while
`test`'s own collector sits four steps from the end, so the hole this PR closes
here stays open there. Correct the comment and leave that job to a follow-up
rather than widen this change into a second job.
Mutations measured red: needs edge deleted; a shared prelude step deleted; the
serializer call site regressed to the array-replacer form; tee reverted to a
bare redirect, which reds both the pin and the executed case.
* ci: restore the blank line separating lint_and_static from web_shell_e2e_smoke
Appending the failure dump and the collector after the pre-existing blank
line left the job ending flush against the next job key -- the only one of
ci.yml's eight job boundaries with no separator. Semantically inert: the
parsed workflow hashes identically before and after.
* ci: correct the HELPER_TESTS file count in the lint lane comment
The comment justifying `lint_and_static` as a required check argues the lane
is not light, citing "the 23-file HELPER_TESTS battery". The env list at
ci.yml:82 enumerates 24 files and is byte-identical to main's, so the count
was wrong on arrival -- in the one paragraph that says it exists so the
required-check follow-up is argued "on true premises".
Same class as the collector comment
|
||
|
|
a8248eaed3 |
ci: collect coverage only on the post-merge run and run cli tests under node (#10890)
Pull-request runs collected v8 coverage for cli, core, web-shell and vscode-ide-companion on every push, uploaded it, and fed it to a coverage comment that failed about as often as it posted; nothing else read the reports. Measured on the cli suite, instrumentation plus the per-file merge cost about a fifth of the wall time. Coverage is now switched on by QWEN_CI_COVERAGE, which the unit-test step sets only for push (main); the upload gate follows the switch, and the comment job and its composite action go. The two workspaces that forced --coverage from test:ci rely on the same switch. The cli package also ran every test file under jsdom although only 90 of the 1002 touch a document. The default is now node; those 90 files carry a `@vitest-environment jsdom` control comment. Together the two changes take the cli suite from 410 s to 290 s on an idle 6-worker host. |
||
|
|
b8f35aa5d3 |
ci: split lint and static checks out of the Test job (#10756)
* ci: split lint and static checks out of the Test job The `test` job bundled two halves with opposite reliability profiles: twenty deterministic lint/static steps (ESLint, actionlint, shellcheck, yamllint, Prettier, schema/notices freshness, lockfile, workspace isolation, TUI dep direction, bundle closure, helper tests, workflow size) and the unit-test run, which was measured failing 41% of post-merge runs on flake alone this week. main still has zero required status checks — a duplicate-declaration compile error shipped to main on 09-01 and sat red for half an hour precisely because nothing gates merges — but the combined job could never become required while the flaky half holds the deterministic half hostage. Move the twenty static steps into a new `lint_and_static` job with the same event surface, the same runner routing, the same release-sync convention (run on skip_ci PRs, no-op per step, so a required context still reports), and its own copy of the checkout/node/install prelude plus the CI-profile classifier (the classifier is one gh api call; duplicating it costs seconds and keeps `test`'s `ci_profile` output — which web_shell_e2e_smoke consumes — untouched). The surgery is a pure move, proven at YAML level: the old job's 40 steps equal the new jobs' union exactly, every step is field-for-field identical to its original, every other job is byte-identical, and `lint_and_static.if` equals `test.if` — pinned by a new equality assertion that rides on test's whole-literal pin, so the two gates cannot diverge silently. Event-matrix A/B against main shows exactly one new row: lint_and_static running on push, pull_request, merge_group and workflow_dispatch, and not on schedule. Mutation-checked: a lint gate that silently drops push, or silently joins the nightly, each reddens two assertions. Scope disclosure: this lane could NOT have caught the 09-01 incident — that was an esbuild transform error inside a test file, ESLint accepts the duplicate binding (verified against the actual broken file), and web-shell's tsconfig excludes *.test.tsx from typecheck. Catching that class needs the unit-test lane, which can only become required once its flake rate is fixed. This split makes the half that CAN be required independent of that work. The size-baseline bump records the duplicated prelude comments; the growth is the split itself. * ci: give lint_and_static a cold-cache-sized timeout The first real run of the new lane was beheaded at exactly 30 minutes: a cold-cache pool runner paid 892s for npm install where the sibling Test job on the same run paid 347s, ESLint and Prettier both ran at ~2x their historical timings, and the job died mid bundle-closure. The 13-15 minute prediction in the split commit came from warm-cache step timings and did not price the first run on any given runner. 45 matches desktop_shell and still surfaces a wedged runner within the hour. * ci: give lint_and_static the contracts its template job already had Nine review findings, one root cause: the split copied test's behavior without copying its guards — every contract test that pins test's checkout, recovery, telemetry, budget and token surface enumerated jobs by name and never reached the copy. Contract extensions: the checkout-ref whole-literal pin now loops both jobs; no-ak-integration-ci's guardCalls gains a lint_and_static entry pinned to test's event-aware expected_sha shape; qwen-triage's unconditional-ownership-restore suite gains the new job; ci-disk-pressure pins the lane's sampler AND its collector — the collector was simply missing (the lane produced the #10035 telemetry and destroyed it with the runner temp dir on the exact ENOSPC death the sampler explains), now added with a per-job artifact name since upload-artifact v4+ rejects duplicates when both jobs fail in one run. New pins beside the gate-equality test: timeout stays the measured 45 (30 was the beheading value), and permissions stays exactly {contents: read} — the workflow grants checks/statuses write, and this lane runs contributor lifecycle scripts. Fidelity fix: the size gate moved back above the dependency install, the fail-fast position it deliberately held in the pre-split job — after npm ci it delivered its verdict ~6 minutes late warm, ~15 cold, and hid size violations behind unrelated install failures. An ordering assertion in workflow-size.test.js pins the position. Drift guard: the merge that landed mid-review proved the copies rot — #10799's trusted-classifier rework updated test's prelude and left the lint copy stale, resolved by hand. A shared-prelude equality test now holds every same-named step field-for-field identical (sole deliberate divergence: the collector's artifact name), so the next drift is a red test, not review archaeology. Prose: the three stale two-job narratives (test's header, HELPER_TESTS, the lanes test file) now describe the three-job reality. All nine mutations red: push-arm drop, verify-head drop, gate reordering, collector drop, ownership-restore drop, timeout lowered to 30, permissions deleted, permissions widened, one-character prelude drift. * ci: route lint_and_static's timeout like its siblings The flat 45 beheaded a second honest run: on cold-cache hk3 pool runners every step lands 2-4x its warm timing (install 940s vs 347s, bundle closure 681s vs 171s) and the serial sum cleared 45 minutes with no step wedged. The pool contention that sized test at 120/60 and no_ak at 60/30 applies here identically; a bare constant priced only the warm half of that reality. ECS 90 / hosted 45, same expression shape as test's, pins updated with both measurements. --------- Co-authored-by: 易良 <[email protected]> |
||
|
|
1e695c34ff |
fix(ci): retain disk samples when npm install fails (#10517)
* ci: gate heavy jobs on a disk floor and persist pressure samples The in-repo slice of #10035: fail fast on a saturated self-hosted host before npm ci instead of dying on ENOSPC mid-run, and keep the disk-pressure timeline from a failed run as an artifact so the peak can be correlated with the job and runner after cleanup reclaims the host. * ci: single-quote the failure() condition to satisfy yamllint quoted-strings Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com> * ci: keep TMPDIR routing block byte-identical across test legs The sample-file setup landed inside the routing block that no-ak-integration-ci pins identical across test/test_macos/test_windows, breaking the identity assertion. Move the DISK_SAMPLES definition and DISKCONTEXT header ahead of the routing block; the DFSAMPLE lines still carry the routed tmpdir. * fix(ci): validate disk floor overrides * fix(ci): reject oversized disk floor overrides Keep the disk-floor shell comparisons inside bash's signed integer range by rejecting numeric overrides that cannot be compared safely. Add the existing helper test coverage for the overflow boundary.\n\nCo-authored-by: Qwen-Coder <[email protected]> * test(ci): pin the disk-floor overflow guard boundaries Pin the three behaviours of validate_floor_override that the current suite leaves unpinned, each verified to catch its mutant: - INT64_MAX is a legal floor and must reach the disk comparison (tightening the length check to -gt 18 now goes red). - Zero-padded overrides padded past 19 raw characters are normalized and accepted (removing the leading-zero strip now goes red). - 20-digit values are rejected by the length branch, which the existing 19-digit case never exercises (widening to -gt 20 now goes red). Co-authored-by: Qwen-Coder <[email protected]> * refactor(ci): simplify disk floor validation and sampling Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): preserve the TMPDIR sampler sentinel Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): retain disk samples from dependency install Co-authored-by: Qwen-Coder <[email protected]> * refactor(ci): reuse the workflow parser in disk tests Co-authored-by: Qwen-Coder <[email protected]> * chore(ci): trigger checks after retargeting to main Co-authored-by: Qwen-Coder <[email protected]> * test(ci): pin disk-pressure workflow contract Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): harden install disk sampling Keep the install sampler non-fatal regardless of command-substitution order and satisfy the regex lint rule in its contract test. Co-authored-by: Qwen-Coder <[email protected]> * ci: record ci.yml growth in the size baseline The disk-floor and disk-sampling legs added ~1 KB of real workflow content; combined with prior main growth this pushed ci.yml 4848 bytes past its recorded 98043, over the 4096 allowance. Bump the ratchet to the current 102891 so the check reflects the reviewed growth. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <qwen-coder @alibabacloud.com> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
3da6d3b4e2 |
ci: allow full test jobs to finish on ECS runners (#10558)
* ci: allow full test job to finish on ECS runners Co-authored-by: Qwen-Coder <[email protected]> * ci: extend full test timeout for slow ECS runs Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
3aa1b14624 |
ci: stabilize tests under shared ECS host contention (#10552)
* ci: tolerate shared runner scheduling delays Co-authored-by: Qwen-Coder <[email protected]> * test(ci): inherit ECS contention budgets Co-authored-by: Qwen-Coder <[email protected]> * test(cli): extend PTY request budget on ECS Co-authored-by: Qwen-Coder <[email protected]> * test(cli): synchronize ACP archive race Co-authored-by: Qwen-Coder <[email protected]> * test(ci): isolate visuals publisher fixture modules Co-authored-by: Qwen-Coder <[email protected]> * test(ci): cover hostile parent package scopes Co-authored-by: Qwen-Coder <[email protected]> * test(sdk-java): isolate finite SSE fixture connections Co-authored-by: Qwen-Coder <[email protected]> * test(cli): make timer-range deadline deterministic Co-authored-by: Qwen-Coder <[email protected]> * test(sdk-java): tolerate shared CI scheduling delays Co-authored-by: Qwen-Coder <[email protected]> * test(sdk-java): preserve timeout contract on CI Co-authored-by: Qwen-Coder <[email protected]> * test(sdk-java): include request submission in timeout bound Co-authored-by: Qwen-Coder <[email protected]> * ci: extend no-AK shared runner timeout Co-authored-by: Qwen-Coder <[email protected]> * test(ci): guard no-AK integration budget Co-authored-by: Qwen-Coder <[email protected]> * ci(sdk-java): serialize ECS test execution Co-authored-by: Qwen-Coder <[email protected]> * test(sdk-java): relax stalled-response setup on CI Co-authored-by: Qwen-Coder <[email protected]> * test(ci): align no-AK timeout contract Co-authored-by: Qwen-Coder <[email protected]> * ci: extend Serve A/B shared runner budget Co-authored-by: Qwen-Coder <[email protected]> * ci: scope extended ceilings to ECS routing The extended job ceilings (test 60->90, integration_no_ak 30->60) were unconditional even though runs-on falls back to GitHub-hosted ubuntu-latest for fork PRs from untrusted authors and when MAINTAINER_ECS_RUNNER_DISABLED=true. Derive each ceiling from the runner classify_pr selected so hosted paths keep the pre-contention bound and a genuine hang there does not burn the extra 30 minutes. The lane contract now evaluates the real timeout expression for both routings instead of pinning a bare number, matching the substitute-then-evaluate technique ci-runner-routing.test.mjs uses on runs-on. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): address closeout review feedback Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
a268b7d175 |
ci: restore the post-merge push trigger on main (#10345)
* ci: restore the post-merge push trigger on main
`ci.yml` dropped its `push` trigger on the premise that the merge queue validates the merged tree before it lands. No merge queue is enabled — no queue run since 2026-07-02 — so nothing validated that tree on either side of the merge: `main` had no gate before a merge, and no check faster than the ~40-minute E2E after one.
Accept `push` on `main` in the `test` job alone. Every other job keeps its existing gate, so a post-merge run is exactly Classify PR + Test, and a nightly still resolves to exactly the two platform lanes. The expensive, PR-shaped steps inside `test` — the no-AK integration gate, the checkout-head verifier and the test reporter — already carry their own pull_request / merge_group gates, so the push lane is lint, static analysis and unit tests.
Route the lane to the ECS pool: a push to `main` is the most trusted context there is, and Test is the ten-plus-minute job that should not spend a scarce hosted Linux runner on every merge. classify_pr's own `runs-on` keeps no push arm by design — it is the canonical association-routing text that sdk-java.yml and serve-ab.yml mirror, and a push carries no author association to route on — so the two halves disagree on push deliberately; a new routing test pins that asymmetry so a later "drift fix" has to read the reason first.
* ci: address review findings on the post-merge push lane
Eight findings from the review pass, all confirmed against the tree.
R1-1 (Critical): the `test` job's checkout `ref:` fell through to `github.ref` on push — `refs/heads/main` resolved at fetch time, a moving tip — while the check run attaches to `github.sha`. The lane could validate a tree it did not report on, and nothing detected it: the checkout-head verifier is gated to pull_request / merge_group and its ancestor check passes for a newer tip regardless. Regressions would still be caught (the tested tree is a descendant), but attribution breaks — red on a clean commit, autofix filed against the wrong merge. Pin push runs to `github.sha`, mirroring the merge_group arm; `e2e.yml` gets the same guarantee from actions/checkout's default. The five other checkout copies sit in jobs that exclude push.
R1-5: the concurrency group was keyed on ref alone, so a post-merge run shared `Qwen Code CI-refs/heads/main` with the nightly (60-minute lanes, neither cancelling on main) and with any PR from a fork branch named literally `refs/heads/main` — a legal ref name that makes the group byte-identical while that run cancels in progress. Scope the group by event, as `e2e.yml` already does.
R1-6: `WORKFLOW_SIZE_BASE_SHA` was empty on push, so both size enforcers failed closed there while PRs stayed green on leniency — the next stale baseline would redden every merge with an error naming a PR that does not exist. Add `github.event.before`; `github.sha` stays rejected because workflow_dispatch would compare a file against itself.
R1-4: the coverage upload ran on push although its only consumer, post_coverage_comment, is gated to pull_request and nothing else reads the artifact.
R1-2: the push lane's YAML wiring had no test witness — the routing tests drive pick_runner's shell directly and stay green if the trigger or either job gate loses its push arm. Add assertions covering the trigger, both accepting jobs, the exclusivity of every other job, and the three fixes above. Each is mutation-checked: removing any one fix reddens exactly one test.
R1-3, R1-7 and R1-8 are three comments the trigger falsified: the failure watcher's "SCHEDULED run only" header, af-025's "ci.yml has no push trigger" premise, and this PR's own "only `test` accepts this trigger", which contradicted the classify_pr arm added in the same diff.
The added prose put ci.yml over its size baseline; trimmed back under the allowance rather than raising the baseline.
* ci: harden the push-lane guards after round-2 review
Five follow-up findings, all on the guards added in the previous commit rather than on the lane itself.
R1-2 (fix-induced): `test`'s push admission was pinned only negatively — no `!= 'push'` — so rewriting `test.if` into allowlist form drops push while every guard stays green: the exclusivity helper skips `test` because it is an expected push job, and the routing tests inject EVENT_NAME=push straight into pick_runner's shell, bypassing the job gate entirely. On a real merge the run would conclude `success` with the lane silently gone, and the failure watcher — gated on `conclusion == 'failure'` — would file nothing. Require the gate to stay exclusion-shaped.
R2-1: the push exclusivity loop was a hand-copy of the nightly one and the two had already diverged — the mirror's allowlist gained `schedule` while the original never gained `push` — so a push-only job would fail the nightly assertion with a message naming the wrong event. Replaced both with one parameterized helper, which also restores the `not.toBe('')` assertion the copy dropped.
R2-2: the concurrency assertion pinned only the added `github.event_name` token. Flipping `cancel-in-progress` to `true` was measured invisible to all 1854 test:scripts tests, while consecutive merges would then cancel each other — the history this repo already recorded once, when 67 of 100 push runs were cancelled and only 25 reported. Pin all three components, the way e2e-workflow.test.js pins its own.
R2-3: the group's pull_request half was still keyed on `head_ref` alone, so same-named branches across forks — and a fork's default branch is `main`, the commonest head_ref there is — share one group and cancel each other's in-progress runs. Add the source-repo term, matching security-checks.yml and serve-ab.yml. Supersede within a PR is preserved: one branch backs at most one open PR per fork.
R2-4: the trigger was pinned by `branches` only; a sibling `tags:` key would widen the lane while the suite stayed green. On a tag-creation push `github.event.before` is all zeros, so WORKFLOW_SIZE_BASE_SHA is unresolvable and both size enforcers take their strict arm, while the failure watcher's `head_branch == 'main'` filter files nothing — a silent red on release day. Pin the whole trigger object.
Mutation-checked: each of the five reddens exactly the intended assertion, and the extracted helper now reports the correct event for a push-only job (verified by injecting a probe job — the nightly assertion passes, the push one fails naming push).
* ci: decide push-lane gates by evaluation, pin them by literal
Round 3 raised seven findings, all Suggestion, and they are all instances of one root cause the review itself named: the guards asserted on substrings, and a substring cannot see a connective. Rather than adding one more substring per mutation, this replaces the two mechanisms behind them.
Gate reachability is now decided by EVALUATING each job's `if` for the event instead of looking for tokens in it — the substitute-then-evaluate technique `.github/scripts/ci-runner-routing.test.mjs` already uses on `runs-on`. That closes R2-1's connective blindness (an `&&`→`||` flip inside an excluded job; an allowlisted equality paired with an event-neutral disjunct under `||`) without the recursive boolean parser the suggestion proposed: a hand-written expression parser is a new fail-open surface of its own, whereas evaluation computes the answer rather than pattern-matching the question. Any context term the substitutions do not model now throws instead of reading as excluded. The helper also asserts the positive direction — that the jobs which must run on an event do — which the token version never did, and which is precisely the "a lane nobody is told about" failure this file's header says it exists to prevent.
Every security-relevant gate is pinned as a whole literal rather than by substring: `classify_pr.if`, `test.if`, the checkout `ref:` expression, the coverage step's `if`, and both halves of the concurrency block. One shape closes R3-3, R1-2, R3-4 and R2-2 together — connector flips, added exclusions, deleted clauses and reordering all change the literal.
R3-2: the trigger comment claimed the test reporter is restricted to pull_request / merge_group. It carries no event gate at all; it is excluded by a same-repo clause that also keeps fork PRs out, so normalising it to the documented allowlist would run dorny/test-reporter on fork events that lack `checks: write`. Reworded per step, with that trap named.
R3-1: bump `.size-baseline` for ci.yml to 85336. The previous two rounds trimmed comments to stay under the allowance, which left 62 bytes of headroom and charged the next PR for growth this one introduced. The baseline file's own header says growth is deliberate and asks for the number to be bumped with a reason — the growth here is the per-finding rationale the review asked for, so it is recorded rather than squeezed out.
Mutation matrix across all three rounds: 22 mutations, none escaping — the seven this round probed as surviving now redden, and the twelve from rounds 1 and 2 still do.
|
||
|
|
385d7cbbda |
ci: serialize helper test files (#10371)
* ci: serialize helper test files * test(ci): pin helper test serialization |
||
|
|
0756be0ce7 |
ci: take the macOS and Windows lanes off pull requests (#10059)
* ci: take the macOS and Windows lanes off pull requests
Both lanes were costing contributors more than they were telling them.
Measured over the 18 hours before this change, on pull requests:
- Windows reported 13 failures and 0 successes. The failures are one
standing set of Windows-only path and symlink cases — resolved paths
into read_many_files, releaseWorktree through an ancestor symlink,
the SHA-256 review worktree — repeating across unrelated PRs, so the
red X almost never belonged to the diff under it.
- macOS queued for a p90 of 42 minutes and up to 159, on a hosted pool
this repository does not saturate by itself: 20 of the 63 sampled
waits happened with zero macOS jobs of ours running.
Neither lane gates a merge — the `main` ruleset carries no required
status check — so none of that waiting or noise was buying protection.
Leave them on the nightly, the merge queue and dispatch, and drop the
pull-request arm plus the classifier job that existed only to feed it.
That makes the nightly load-bearing rather than a backstop: it is now
the only report of a non-Linux regression, so add a guard for the three
ways it could go quiet — the schedule disappearing, a lane no longer
accepting it, or a third job joining it and failing the run for reasons
that have nothing to do with either platform.
The classifier, its script mode and both test files are left in place so
restoring the pull-request trigger, once the Windows failures are fixed,
is a revert plus two `if` arms.
* ci: pin the retired-classifier contract in ci-platform-lanes.test.js
The lane change left scripts/tests/ci-platform-lanes.test.js pinning the
shape it removed — a sensitive-PR trigger arm and a live classify_platform
job — which failed the Test job on this branch. Rewrite the suite to pin
the new contract instead: both lanes run on the schedule, the queue and
dispatch and on nothing else; the pull-request arm and the classifier are
gone whole (a half-restoration — a trigger without its classifier, or the
reverse — fails); the classifier's own scripts stay tested so restoring
the trigger stays a clean revert; the nightly still reaches exactly the
two lanes and its failure still files an issue.
That suite already owned the nightly-liveness assertions, so the
platform-lane-triggers.test.mjs guard added earlier on this branch
duplicated it — dropped, along with its HELPER_TESTS entry.
Verified by mutation: deleting the schedule, dropping schedule from one
lane, letting a third job onto the nightly, and reintroducing the
pull-request arm without its classifier each fail the suite; the branch
shape passes 17/17.
|
||
|
|
519e824dc7 |
fix(ci): give the macOS and Windows lanes a trigger again (#9370)
* fix(ci): give the macOS and Windows lanes a trigger again Both lanes are gated on `merge_group`, and no merge queue is enabled on this repository — the `main` ruleset carries only deletion, non-fast-forward and pull_request rules, no status check is required, and merges land as squashes. The last `merge_group` run of anything was 2026-07-02. So the gate was an off switch: the lanes reported as "skipped" on every pull request, which reads as agreement, and nothing ever reached them afterwards. The only signal this repository has about a host that is not Linux with a GNU userland had been silently off for six weeks, which is how #9220 shipped a GNU-only `realpath -m` in a workflow guard with the suite that pinned it red on every Mac. Three triggers now, in cost order. A pull request whose diff a new classifier recognises as platform-sensitive: shell scripts of every dialect, workflow and composite-action YAML and the scripts they call, the script layer and its tests, the test-runner configuration that decides which suites run where, the root manifests, and source paths whose segments name a platform-coupled subsystem. It is a net, not a proof — it cannot see a platform assumption inside an ordinary source file, and no path rule ever will — so every unknown answers "sensitive": an unreadable listing, an unparsable entry, a fork pull request, a truncated file list, or the classify job failing outright all end as "run the lanes". Only a confident `false` skips them. The merge queue, if it is ever enabled again, unchanged. And a nightly run on `main` for everything the path list cannot see. Every other job in the workflow excludes `schedule` explicitly, so a nightly is exactly two jobs, and 'Qwen Code CI' joins the workflows the main-failure watcher opens autofix issues for — a red lane nobody is told about is the same silence the queue gate produced. That watcher gains a trigger-level `branches: ['main']` filter so the CI workflow's pull-request completions do not raise an event there just to skip. The classifier runs in its own small hosted job rather than as a step in `classify_pr`: that job's outputs pick the Linux runner for the whole run, and this one needs a checkout — on a pool whose workspace other jobs have poisoned before. It checks out the pull request's BASE commit, never the head: it runs before any review and executes a script from the tree it checks out. Its listing goes through the existing classify-pr-profile.sh wrapper, extended with a mode argument, because that wrapper's whole point is that one PR is never listed twice and classified differently in two places. Twelve tests pin the wiring — the triggers, the fail-safe direction of the gate, the base-commit checkout, that a nightly stays two jobs, and the alerting — and ten more pin the classifier itself, including the substring traps (`Shellfish.tsx`, `cryptic.ts`, `plateauDetector.ts` must not drag both lanes in) and every fail-safe path. Mutation-checked: flipping the gate to `== 'true'`, dropping the schedule from a lane, dropping the nightly guard from the ubuntu job, pointing the checkout at the head, dropping the classifier's test from the helper list, and removing the schedule trigger are each caught. * fix(ci): gate the Windows lane's checkout verification per trigger The first thing the revived triggers hit was not a test failure but the lane's own plumbing. `test_windows` verifies its checkout with `verify-checkout-head`, and the input was written when this lane ran in the merge queue alone: `expected_sha: github.event.merge_group.head_sha`, with no event gate. On a pull request that expression is empty, the action refuses an empty SHA, and the lane went red in 63 seconds without running a test — the first Windows run in six weeks, failing on the trigger rather than on the code. Give it the event-aware shape the Ubuntu gate already uses, and skip it where there is nothing to verify: the scheduled and dispatch runs check out a branch by name, not a head commit. Pinned generally rather than by name: for both lanes, any step whose inputs read a `github.event.<event>` context must be gated to that event, in the step's own `if` or in the expression itself. Restoring the old spelling turns that test red. * fix(ci): close review round on the revived platform lanes (#9370) * fix(ci): pin the Windows lane routing to the canonical trust policy (#9370) * fix(ci): host-probe the macOS lane's bash and GNU dependencies (#9370) The revived macOS lane ran the shared suite for the first time since the merge queue went dark and failed on exactly three tests, identical across three consecutive runs: the bite-check block and the baseline A/B green path die with `mapfile: command not found` (a bash >= 4.4 builtin; macOS ships 3.2), and the health-probe repair case trips over BSD `mktemp -u` attempting to create where GNU's print-only `-u` just names the canary. Both suites pin scripts that only ever execute on Linux runners — ubuntu-latest or the Linux ECS pool — so the defects cannot exist in production; the suites just cannot run those scripts on a macOS host. Follow the convention #9220's fix established for the realpath case in the same file: probe the host capability, not the platform, and skipIf the three dependent tests where it is absent. A Mac with a newer bash or GNU coreutils fronting PATH keeps the coverage; the Linux lane runs all three unconditionally. The large bite-check test body is re-indented by prettier around the new skipIf wrapper; no other token changed. * fix(ci): stop the subsystem rule matching compounds that name something else The platform-sensitivity classifier split a path segment on dashes and underscores anywhere, so `packages/web-shell/**` matched the `shell` keyword — one of this repository's largest packages, a browser UI with no host coupling, summoning both expensive lanes on every change to it. That is the cost the gate exists to avoid, spent on the wrong diffs. A keyword now counts when it NAMES the thing: a whole path segment (`src/sandbox/**`, `platform/paths.ts`, `shell.ts`) or the head of a hyphen/underscore stem (`pty-host.ts`). Not a trailing part of a compound, which belongs to whatever the leading word names, and still not a substring inside a longer word. Pinned both directions, including a directory that IS named for the subsystem wherever it sits (`web-shell/components/shell/**` stays sensitive). Mutation-checked: dropping either rule, or restoring the split-anywhere spelling, turns the suite red. Also drops a wrong issue citation in the watcher test's comment: the nightly comes from this change, not from the wipe-guard back-port. * test(ci): pin the gate as a disjunction and the watcher's name binding Two mutation-survivable gaps in this PR's own tests. The clause-presence assertions left a connective mutation alive: `||` → `&&` between two event clauses keeps every asserted string in place and makes the gate unsatisfiable for every trigger — both lanes silently off again, which is the state this PR exists to end. Read the event group and require it to be a disjunction, allowing `&&` only inside the pull-request clause that binds to the classifier output. And the watcher's binding to this workflow is by display name: `workflow_run.workflows` matches the watched workflow's `name:`, so renaming ci.yml unhooks the nightly's alerting silently. Pin both sides. Both checked by mutation: flipping one `||` and renaming the workflow each turn a named test red. * fix(ci): close the classifier's CRLF gap and widen the lane step scan Three findings from this round, all in the direction of the tests and the parser being less clever than they claimed. The classifier's JSONL reader split on `\n` while its sibling splits on `/\r?\n/`. Every suffix rule here is end-anchored, so one carriage return on a CRLF listing would leave `build.sh\r` and classify a script-layer change as ordinary source. Matched to the sibling, with a fixture on both the JSON and the raw-line path. The per-trigger step scan serialized only `with:` inputs, so the same defect wearing an `env:` or `run:` key escaped it; it now reads all three. And the nightly blast-radius guard tested for the MENTION of an allowlisted event rather than the IMPOSSIBILITY of `schedule` — a job gated `pull_request || schedule` satisfied it while running every night. It now requires the absence of an explicit schedule clause too. * style(ci): format the runner-routing suite The Windows trust-policy matrix added last round left the file outside prettier's style, which the repository's lint step fails on; main's copy is clean. Formatting only — the nine assertions are unchanged and still pass. * fix(ci): gate the two mapfile-crossing gate tests on the host probe (#9370) * fix(ci): keep pull requests off the persistent Windows pool and cover native audio (#9370) A pull_request run executes the workflow YAML from the PR's own merge commit, so the test_windows runs-on trust clause it evaluated could be rewritten by any PR the lane admits. Every pull request now runs on hosted windows-2022 unconditionally; the pool is reached only by the post-approval merge queue, schedule and dispatch, guarded by the kill-switch. The routing tests and the exact-line pin are re-pointed at that enforceable shape. Also add audio to the platform-sensitivity classifier's subsystem keywords: packages/audio-capture is a node-gyp workspace compiled per-host on exactly the two revived lanes, but its native sources (.cc/.mm/.gyp) carried no rule and a PR touching only them skipped both lanes. The workspace directory now classifies sensitive; an ordinary .cc elsewhere stays ordinary source. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |