Commit graph

33 commits

Author SHA1 Message Date
易良
8a21551599
chore: enforce Playwright version parity and close the deferred #11101 follow-ups (#11371)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Lint & Static (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / web-shell E2E Smoke (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (no-AK, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (ubuntu-22.04) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (windows-2022) (push) Blocked by required conditions
E2E Tests / Build for E2E (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/1 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 1/2 (push) Blocked by required conditions
E2E Tests / E2E Test - macOS - shard 2/2 (push) Blocked by required conditions
E2E Tests / E2E Interactive - OpenTUI renderer (bun) (push) Blocked by required conditions
E2E Tests / channel-plugin E2E (nightly) (push) Blocked by required conditions
E2E Tests / cron-interactive E2E (nightly) (push) Blocked by required conditions
E2E Tests / web-shell Browser Regression (push) Waiting to run
npm cache producer / Save npm cache (push) Waiting to run
pnpm Worktree Smoke / Install (macos-latest) (push) Waiting to run
pnpm Worktree Smoke / Install (ubuntu-latest) (push) Waiting to run
pnpm Worktree Smoke / Install (windows-latest) (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
* chore: enforce Playwright parity and close deferred #11101 follow-ups

Works the four items tracked in #11336:

- Pin Web Shell's @playwright/test to the same exact version as the root
  playwright dependency, and fail `npm run check:lockfile` when the two
  direct manifests drift apart or their resolved root trees stop matching
  the pin. The check deliberately tolerates the packages/mobile-mcp tree:
  mobilewright depends on playwright and playwright-core at an exact
  older revision, so no manifest edit in this repository can dedupe it.
- Correct the "Install evidence browser" rationale in qwen-triage.yml and
  its restatement in the workflow test. The two-trees premise no longer
  holds now that both manifests are pinned; the resolution behavior is
  unchanged, since resolving from the harness's own directory still keeps
  the binary tied to the package it imports.
- Cover the ENOENT fallback of the exported isMainModule helper. The
  existing runner.py assertion takes the realpath success path because
  that file exists, so the fallback stayed unpinned.
- Separate the CLI-only from the daemon-only followup suggestion gates in
  the documentation instead of pointing daemon clients at the blended
  CLI-oriented list, keeping the headless/SDK opt-out guidance.

* chore: pin the Playwright parity check and correct its scope notes

Follow-up to the round-1 review on this PR.

The parity block had no test in either direction: the only suite that runs the
script asserted the two pre-existing banners, so deleting the block, inverting a
comparison, or breaking the importer derivation left both `npm run test:scripts`
and CI's `check:lockfile` gate green. That is the defect class this PR exists to
remove — the same diff rewrites two rationale comments to lean on the check, so
an unpinned guard rots into a comment that lies. Add the third banner to the
existing suite and a fixture suite that runs the script against perturbed copies
of the real manifests and lockfiles, covering both drift arms and the two shapes
that must stay green.

Also correct three properties of the block the review found:

- The inventory comment claimed two Playwright declarations sit outside the
  invariant; `packages/mobile-mcp` declares `@playwright/test` as a range
  directly, in a workspace member, and was not named. It is the only in-workspace
  range on a name this check pins, so it is what the resolved-version message
  should point an operator at.
- The agreement check destructured exactly two entries while the comment invited
  maintainers to extend the list, so a third entry would have received the
  exactness check and silently skipped the comparison the block exists for.
  Compare every entry against the first and derive the resolved-tree locations
  from the same list instead of repeating them as a second literal.
- The pnpm arm prescribed "regenerate it" for every divergence, but
  `pnpm-workspace.yaml`'s `overrides:` and `.pnpmfile.mjs` both rewrite the
  recorded specifier, so regeneration is a no-op for those. Name the deciding
  layer in the messages and the importer list's source for a missing entry.

* docs: stop counting the followup suggestion code paths

Merging main brought in the OpenTUI port of ink's AppContainer effect
(packages/cli/src/ui/opentui/followup-generation.ts), which is a third
non-test `generatePromptSuggestion` call site. The section opener claimed
"Two code paths decide this", which a grep now contradicts even though the
substance still holds: the OpenTUI hook applies the same CLI gate set as
AppContainer — interactive, not SDK mode, the Responding to Idle edge, no
error in the last item, no waiting calls, approval mode not plan — and
collapses ink's five explicit dialog checks into `waitingCalls.length === 0`
because the composer is unmounted while a shell dialog is open.

Name the two decision-makers instead of counting implementations, so the
sentence stays true when a renderer is added or retired. The CLI/daemon
split below is unchanged and was re-verified against the merged tree: the
ink gate is byte-identical at AppContainer.tsx:3612-3627, the daemon gate
moved only by line number (Session.ts:5080-5095), and the shared
MIN_ASSISTANT_TURNS gate is still at suggestionGenerator.ts:83,103.

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-09-12 11:40:46 +00:00
易良
585589ac67
fix(dev): install hooks during worktree bootstrap (#11496)
* fix(dev): install hooks during worktree bootstrap

* fix(dev): keep worktree hook setup out of the shared git config

Husky runs `git config core.hooksPath .husky/_` with no --worktree, so
from a linked worktree the value lands in the config every worktree of
the repository shares while `.husky/_` is created only in the checkout
being bootstrapped. Skip the Husky step and report it when the key is
unset and this checkout does not own the repository config, so a
bootstrap can no longer repoint hook resolution for roots that never
received the wrappers. A primary checkout still installs hooks, and an
already-configured `core.hooksPath` is untouched.

Also drop the caller's success exit, which `install()` made unreachable
when it started exiting on every successful path, and bring the
pnpm-worktree-bootstrap design doc in line with a hook step it still
recorded as deliberately skipped.

The new fixture runs the real script against a throwaway root whose
`.git` is a file or a directory and whose config comes from a real
`git init` repo, which makes both new branches reachable and pins the
fail-closed guard: the injected `GIT_CONFIG_*` constant holds one value
for the child's whole lifetime and cannot express the unset state that
asks husky to write.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtuo29vduf

* fix(dev): ask git which root owns the worktree hook config

`ownsRepositoryConfig()` inferred repository-config ownership from the
filesystem shape of `.git`, and the proxy is wrong at both ends. With no
`.git` at all, `statSync(..., { throwIfNoEntry: false })` returns `undefined`
and the predicate folded that absence into "owns the config", so a
repository-less checkout ran husky into its `.git can't be found` soft failure
(exit 0) and the fail-closed check then turned a successful dependency install
into exit 1 blaming Husky. A `.git` file is not only a linked worktree either:
`git clone --separate-git-dir` checkouts and submodules have one too and do own
their config, so hooks were declined where they would have been correctly
scoped.

Ask git instead: `rev-parse --git-dir` differs from `--git-common-dir` only in
a linked worktree, and a failed `rev-parse` names the no-repository state, so
the skip notice stops asserting "linked worktree" and the bootstrap does not
gain a hard git dependency.

Also bind the fail-closed check to an artefact husky's own write produced, not
only to the config value. husky 9.1.7 exits 0 on every soft-failure path
(`index.js:16` git command not found, `index.js:17` refused `git config` write)
before the `mkdirSync(_())` on line 19, and a linked worktree inherits
`core.hooksPath` from the config it shares, so re-reading that value compared
it against itself and passed exactly when husky had created nothing.

Restore the registry-fallback case's hermeticity: `PATH` holds only the stub
directory again, which now also pins that the retry needs no ambient git.
Rebuild the ownership fixture from real git layouts, because `rev-parse`
resolves nothing for a `mkdirSync`'d `.git` or a hand-written `gitdir:` file,
and give the stub husky a failing mode so the exit code husky returns is pinned
rather than the install result's.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtuys2rjuu

* fix(dev): surface hooks-path read failures in worktree bootstrap

- getHooksPath() no longer collapses a refused git config read (exit 128/2/3) into "unset": only an absent key (exit 1) or a missing git binary keeps the skip path; anything else fails the bootstrap with the read error instead of a green, hook-less worktree.

- The linked-worktree skip notice now names the recovery path: re-run this script once the primary checkout has hooks installed.

- Tests cover a git stub exiting 128, the real-world unset HUSKY state, and pin the recovery sentence in the skip notice.

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-10 09:44:47 +00:00
易良
078b924989
refactor(ci): extract release workflow scripts (#11165)
* refactor(ci): extract release workflow scripts

* test(ci): pin release-note labeling to the extracted step script

release.yml no longer inlines the auto-labeling body: the publish step now
just dispatches to run-release-step.sh, which owns the git rev-list
enumeration and the classify-release-notes.mjs call. The wiring test kept
asserting those four strings against the step's run block, so it failed on
the dispatcher path alone.

Read run-release-step.sh and assert the labeling body there, and assert the
dispatch from release.yml. The workflow-level guarantees (step name,
continue-on-error, GITHUB_TOKEN env, issues/pull-requests write permissions)
and the .github/release.yml exclusion entry are unchanged.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmtpmlcodmz

* test(ci): repoint release assertions at the extracted step script

The extraction moved the GitHub Release notes, standalone archive, and
release-failure autofix label logic out of .github/workflows/release.yml
into .github/scripts/run-release-step.sh, but three consumer suites still
pinned the moved text in the YAML, so `npm run test:scripts` was red and
the release workflow's fail-closed `quality` gate would refuse to publish.

Read the step script in ai-release-notes-workflow, install-script, and
qwen-autofix-workflow and assert the moved strings there with their new
lowercase locals (`notes_args`/`notes_file`), keeping on release.yml only
what genuinely stayed in it. Restore the "Safe to auto-apply approval"
rationale above both AUTOFIX_APPROVED_LABEL writes in the notify-failure
arm; it is the only recorded justification for the workflow granting
itself autonomous autofix approval.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtq0vr74ni

* test(ci): derive the ECS wipe subject set from the runner, not the wipe

Selecting checkout jobs by "has a Restore workspace ownership step" made
the subject set a function of the property under test, so a job added to
the ecs-qwen-hk4-host pool with an actions/checkout and no wipe was
filtered out before any assertion ran. Key the exemption on the pool
marker instead, and pin that every pool-routed job carries the wipe
whether or not it checks out.

The hosted label cannot be the discriminator: every pool-routed runs-on
expression names 'ubuntu-latest' as its fallback branch.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtq0vr74ni

* test(ci): re-scope the release-creation token pin to its own step

The workflow-wide toContain was satisfied by the second CI_BOT_PAT
occurrence on "Trigger ECS runner qwen update", so flipping the token on
"Create GitHub Release and Tag" to github.token left every lane green.
Restore the step scoping and the deleted rationale comment in release.yml.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* fix(ci): fail the release step runner closed on pipeline legs

run-release-step.sh set bare `set -e` while both sibling runners set
pipefail. In notify-failure a connection-level gh failure inside
`gh issue list ... | jq -c ...` leaves jq exiting 0 on empty input, so the
empty result took the create-a-new-issue branch and filed a duplicate
autofix/approved release-failure issue past all three reuse guards.

Pin the flag next to the existing sibling pin and add a behavioural row for
the notify-failure arm with an unreachable gh.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* test(ci): relocate the release timeout value-format contract

This PR condensed the nine-line release.yml lane comment to "values are
minutes" and relocated the format half nowhere: grep for the deleted claims
(expression error / free text / truthy / leading number) found no copy under
.github/, scripts/tests/ or docs/. Put the contract beside the assertions that
already pin the three tunable lanes' expressions, since release.yml is
size-pinned twice.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* docs(ci): scope the workflow_sha claim to the extracted helpers

The design doc said the trusted checkout "prevents an operator-selected
release ref from supplying code that receives release credentials". Two paths
at HEAD contradict that reading: resolve-version executes the ref's
scripts/get-release-version.js under the job token (run-release-step.sh:84),
and push-release-branch commits with core.hooksPath .husky (:116, :130) in a
step whose env carries CI_BOT_PAT (release.yml:659). Both predate this
extraction, so name them as residual paths rather than as prevented ones.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtqfvvmto2

* fix(ci): format the release-version test to satisfy the Prettier gate

`Lint & Static` failed at the `Run Prettier` step: a continuation line in
the `assert-unreleased` format-gate test carried four extra spaces.

* fix(ci): exclude nested .git dirs from the docker build context

The extraction added a `.release-workflow/` checkout to `integration_docker`
between the sandbox build's reset and the build itself, and `.dockerignore`
listed `.git` only in its root-anchored form. A depth-1 sparse checkout of
the pinned ref is 1.1M of worktree over 51M of git objects, so every
uncached sandbox image baked those objects into a discarded builder layer on
the shared pool this job is routed to. `node_modules` and `dist` already
carry both forms.

* fix(release): close four gaps the script extraction left behind

R1-15: the push-time guard returned exit 2 for a malformed version, and
run-release-step.sh treats 2 as a transient probe failure — so a refusal
that can never succeed was retried three times over ~45s and logged as a
connectivity problem. Malformed versions now exit 4, which the retry loop
breaks on immediately and which is not the exit-3 version_refusal marker.

R1-16: the extraction moved the guard behind `.release-workflow/` so the
decision comes from the workflow-pinned SHA, but left a working
`--assert-unreleased` dispatch in the ref-supplied get-release-version.js.
A future step using that spelling would load the selected ref's guard.
Removed it, and re-pointed its five tests at the real entry point.

R1-13: assert-release-version.mjs ships alone under release.yml's
sparse-checkout set, so it cannot import release-helpers.js — the
duplicated isExpectedMissingGitHubRelease is deliberate. Documented that
at both sites and pinned the two bodies identical, so widening only the
guard's copy (which would read a rate-limited probe as "release absent")
goes red.

R1-6: restored the notify-failure arm's rationale, matching the condensed
comments the other five arms kept: the exact-title re-check that stops a
v0.18.1 failure reusing a v0.18.10 issue, the bot-author preference, and
the re-query that keeps autofix off a maintainer-owned issue.

* fix(release): re-pin the trusted runner before every step that runs it

R1-10: `Build Bundle and Prepare Package`, `Build Standalone Archives` and
`Verify Standalone Archives` executed `.release-workflow/.../run-release-step.sh`
from a tree that selected-ref npm code had already run over — `build-package`
for the first two, and `npm publish`'s own lifecycle scripts for the third,
inside the job that holds `id-token: 'write'`. At the merge base these were
inline `run:` bodies sourced from the workflow file, immune to anything
written into the working tree, so the exposure is introduced by the
extraction. Each now gets the reset-and-re-checkout pair the design doc
already promises for credential-bearing steps.

The guarding test hand-listed six protected step names, so it could not fail
for the three it omitted. It now sweeps every step whose `run` mentions
`.release-workflow/` (17 today) and separately pins that each re-checkout is
preceded by a reset.

R1-5: nothing tied the publish allowlist to the guard's `PUBLISHED_PACKAGES`
except a comment pointing at publish steps this PR deleted from release.yml.
A channel added to the loop but not the array would ship unprobed, so a retry
of a partial release reports "unreleased" and force-pushes over the tip the
shipped package anchors to. The two sets are now pinned against each other,
and the stale comment points at the real pin.

The three inserted step pairs grow release.yml by 294 bytes (well inside the
4096-byte allowance); the ratchet baseline and the PR description move with
it rather than letting the recorded number drift.

* test(release): pin the flags, arms and modes the extraction left unwitnessed

R1-19: the arm's only executable probe stubbed npm with a script that
recorded `$PWD` and discarded every argument, so deleting `--tag=${NPM_TAG}`
stayed green while `npm publish` would fall back to the `latest` dist-tag —
handing the 21:00 UTC nightly to every end-user install and to the ECS fleet
updater. The stub now records the argv and each publish is checked for
`--access public` and the dist-tag.

R2-3: `resolve-version` was the one arm no test executed or text-pinned;
flipping `--type=preview` to `--type=stable` survived all 2233 scripts tests,
while at runtime the stable path ignores `preview_version_override` and would
publish an operator's manual preview as a stable release. Two execution tests
now cover the preview mapping and the malformed-version rejection; both were
run against the real arm before pushing.

R1-18: release.yml invokes these scripts by bare path, so the executable bit
is load-bearing, but every test invokes them as `bash <script>`, which
ignores the mode — and the workflow triggers only on schedule/dispatch, so no
pull-request lane runs the bare-path form. A mode-normalizing commit would
keep the suite green and kill `set-flags` with exit 126. The recorded git
mode is now pinned for all three scripts.

* test(release): pin the trusted prefix, the fork gate and the real version formats

R1-3: every arm pin elsewhere is a substring match that a bare
`.github/scripts/run-release-step.sh` invocation would satisfy just as well,
so nothing pinned the mechanism the extraction exists for. All 17 step
invocations are now required to carry the `.release-workflow/` prefix and
forbidden from naming the release-ref copy.

R1-9: `PUBLISH_AUDIO_CAPTURE` replaced a deleted step-level
`if: github.repository == 'QwenLM/qwen-code'` gate and, unlike its
`PUBLISH_EXTERNAL_CONTEXT_MEM0` sibling, had neither its gate expression nor
its true branch pinned — a fork running this workflow would publish
@qwen-code/audio-capture. Both halves are pinned now.

R1-14: the format gate is the guard's only genuinely new behaviour and its
accept path was exercised solely with '1.2.3', while the two scheduled
releases produce nightly and preview strings. Both formats are now asserted,
so tightening the pattern fails here rather than at 21:00 UTC.

* fix(release): keep failure reporting alive when the trusted runner is not

R1-11: `notify_failure` gained a checkout of `github.workflow_sha` as its
first step, and its whole body now lives in a shell script. At base the job
had no checkout at all and the notifier was inline, so the job whose only
purpose is reporting every other job's failure acquired two ways to fail
silently: a degraded git backend takes out the checkout, and a syntax error
or dropped exec bit in run-release-step.sh takes out every arm including
notify-failure. In both cases `Create Issue on Failure` carried the default
`if: success()` and was skipped — no issue, no autofix label, no dispatch,
and the scheduled autofix fallback scans issues, so nothing recovered it.

Rather than moving 105 lines of notifier back into the workflow — which the
orchestration guard in this PR forbids at 12 run-lines per step — the primary
path keeps the extraction and stops being load-bearing: it runs under
`always()`, absorbs its own failure, and a five-line inline fallback files a
plain issue whenever it did not succeed. The fallback depends on neither the
checkout nor the script, does no reuse, labelling or dispatch, and accepts a
duplicate issue as the cost of never going silent.

The workflow's line ceiling moves 800 -> 830 for those lines, with the reason
recorded at the assertion; the per-step run cap that keeps logic out of the
YAML is unchanged and still holds. Ratchet baseline and the PR description
move with the file.

* style(release): apply the pinned Prettier to the fallback notifier

Prettier 3.6.1 normalizes the quoting on the new `if:` and drops a
trailing space; the ratchet baseline follows the two-byte change.

* docs(release): name exit 4 where the guard's exit codes are described

Adding the malformed-version code left two descriptions behind: the retry
loop had no statement of the contract at all, and the test comment beside it
still said exit 0 and exit 3 were the only decisive outcomes.

* fix(release): avoid duplicate fallback failure issues

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: yiliang114 <jinjing.zzj@gmail.com>
2026-09-08 07:09:09 +00:00
易良
3ecfaffdf8
perf(dev): add pnpm worktree bootstrap foundation (#10449)
* perf(dev): add pnpm worktree bootstrap foundation

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

* fix(dev): harden worktree bootstrap process handling

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

* perf(dev): prefer cache-only worktree installs

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

* fix(dev): preserve cached install cancellation

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

* fix(build): harden pnpm worktree bootstrap

Validate pnpm lock updates in releases and exercise real installs and builds across supported hosts. Preserve npm release compatibility and keep dependency-only setup from rewriting npm-layout notices.

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

* chore: record merge attribution

Record the required attribution for the Stage 1 merge without rewriting published history.

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

* fix(dev): keep worktree bootstrap clean

Use a bootstrap-scoped notice guard because nested npm lifecycle commands replace npm_lifecycle_event. This preserves explicit notice generation while preventing dependency setup from rewriting tracked output.

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

* ci: track pnpm smoke workflow size

Register the new workflow in the repository size ratchet as required by the main CI gate.

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

* fix(ci): drop stale importers from the new pnpm lockfile

The lockfile was generated before the WebShell cutover (#9811) removed
@qwen-code/webui from web-shell and the tailwind tooling plus
@qwen-code/webui from vscode-ide-companion, so frozen-lockfile
installs fail on all three smoke platforms with
ERR_PNPM_OUTDATED_LOCKFILE. Drop the five stale importer entries so
the lockfile matches the current package.json manifests; verified with
pnpm 11.24.0 install --frozen-lockfile --lockfile-only.

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

* fix(ci): refresh pnpm lockfile after merging main

The merge of origin/main added remend@^1.3.1 to packages/cli/package.json
without updating pnpm-lock.yaml, breaking the pnpm Worktree Smoke workflow
frozen-lockfile install. Regenerated with pnpm install.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmtid3rse9h

* fix(scripts): resolve the path variable case-insensitively on Windows

A spread of process.env is an ordinary object, and native Windows shells
expose the path variable as `Path`, so `env.PATH` was undefined there and
findOnPath never located corepack — the offline-first Corepack bootstrap
silently degraded to npx on exactly the hosts it exists for.

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

* test(scripts): pin the worktree bootstrap guards against mutation

- Split the fallback log on /\r?\n/ like the sibling cmd.exe mocks, so the
  assertion holds on the Windows lane's CRLF output.
- Add a win32 variant bootstrapping with the native `Path` casing to pin
  the case-insensitive lookup.
- Assert the smoke workflow's fail-fast flag, the install-before-clean
  step order, and the no-build guard via a substring on the raw job text.
- Extend the pnpmfile rewrite fixture to devDependencies and
  optionalDependencies, which the committed lockfile already uses.
- Assert the notice-skip guard by effect (writeFile never called) and add
  the flag-absent companion test with I/O stubbed.

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

* chore(deps): sync pnpm-lock.yaml with qwen-live ACP backend deps

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmtjyz3tdco

* fix(dev): stop patch-package from corrupting the pnpm store

The root postinstall runs patch-package, which rewrites files under
node_modules in place. With pnpm's default import method those files are
hard links into the content-addressable store, so the patch rewrites the
store entry too and its contents stop matching the sha512 it is filed
under. Every subsequent fresh worktree then fails its --offline stage
with ERR_PNPM_NO_OFFLINE_TARBALL, silently falls back to the registry
(exit 0), and the fallback's postinstall corrupts the store again, so the
offline path never hits. The advertised offline timing was only reachable
on a same-tree reinstall, which needs no relinking.

Set packageImportMethod to clone-or-copy: a copy-on-write clone where the
filesystem supports it, a plain copy elsewhere. Either way patch-package
edits only the worktree's own copy and the store entry stays intact, so
later worktrees resolve entirely from the store.

Verified on an isolated fixture store: under the default method the
node_modules file and its store entry share an inode, and an in-place
edit moves the store file's sha512 off its address, after which a fresh
offline install fails with ERR_PNPM_NO_OFFLINE_TARBALL; under
clone-or-copy the same edit leaves the store hash unchanged and the fresh
offline install reports reused 1, downloaded 0.

Also add pnpm-lock.yaml to .prettierignore. prettier reflows it into a
shape pnpm does not emit, so `npm run format` and `pnpm install` fight
over the file; .yamllint.yml already ignores it for the same reason.

Both facts are pinned by tests in scripts/tests/package-scripts.test.js.

Claude-Session: https://claude.ai/code/session_012797rgiteWJxLT9TLkKq8G

* fix(ci): refresh pnpm-lock.yaml for qwen-live prompts deps

Main added prompts/@types/prompts (and the ACP sdk) to
packages/qwen-live/package.json after this branch generated its
lockfile, so the pnpm Worktree Smoke frozen-lockfile install failed
with ERR_PNPM_OUTDATED_LOCKFILE. Regenerated with pnpm 11.24.0
(--lockfile-only); frozen-lockfile and supply-chain policy checks pass.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmtlc9sozfg

* fix(pnpm): close review findings on worktree bootstrap

- Declare @qwen-code/qwen-code-core in vscode-ide-companion so the pnpm
  linker materializes it; regenerate both lockfiles and assert the link in
  the smoke workflow (R5-1)
- Pin @types/node 20.19.1 in packages/core so the pnpm layout compiles
  against the same types npm hoists (D6-1)
- Accept and pin the corepack +sha512 integrity suffix on packageManager;
  strip it for the npx fallback spec (D6-2)
- Cover every workspace member in the pnpmfile rewrite set, mirror the npm
  channel list in pnpm-workspace.yaml, and cross-check both in tests
- Run setup-worktree.js installs with cwd pinned to the checkout, gate the
  smoke clean-check on git status --porcelain, stop post-merge runs from
  cancelling each other, and extend check-lockfile.js to pnpm-lock.yaml

* merge(main): realign pnpm workspace set and lockfiles

Main removed the webui and cua-driver packages; drop both from the pnpm
rewrite set and regenerate the pnpm lockfile against the merged manifests
(picks up playwright, react-markdown, and the other main-side dependency
changes) so the frozen bootstrap matches what the PR merge ref will run.

* style(ci): quote smoke workflow scalars per yamllint

* fix(dev): require Corepack for pnpm bootstrap

* fix(dev): close pnpm bootstrap review blockers

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: yiliang114 <jinjing.zzj@gmail.com>
2026-09-08 03:07:40 +00:00
Shaojin Wen
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.
2026-09-03 08:49:43 +00:00
易良
035418958d
fix(ci): retry a contended unit attempt and bound a hung one (#10868)
* fix(ci): retry a contended unit attempt and bound a hung one

The same third of the same suite measures 6.7 or 36 minutes on this fleet
depending only on which host it lands on, and the same commit run three
times failed three disjoint test sets. A retry lets that pass while a real
break still fails every attempt, which is how the release lane already
runs. A step timeout turns the 60-minute stall in #10490 into a legible
failure instead of a job cancellation with orphaned processes.

Splitting `test:ci` into a workspaces half and the scripts half is what
lets the flag reach every workspace's vitest: the combined script ends in
`&&`, so an appended argument would have reached only `test:scripts`.

Claude-Session: https://claude.ai/code/session_01MCE9CXnMVrX4fUxHpQoUr8

* fix(ci): size the unit-step cap for three forks, not six

The ~32-minute figure the 90-minute cap was drawn from was measured at six
Vitest forks. The pool now runs three, which roughly doubles the test phase
on an idle host, and a failing run adds its retries on top — so 90 could
have cut off a slow-but-live run, which is the outcome the cap exists to
avoid creating. 110 still fits the job's 120-minute budget.

Claude-Session: https://claude.ai/code/session_01MCE9CXnMVrX4fUxHpQoUr8

---------

Co-authored-by: yiliang114 <jinjing.zzj@gmail.com>
2026-09-03 03:55:58 +00:00
易良
3e8d92a729
fix(release): stop hiding sandbox build failures and check the prepared package early (#10784)
* fix(release): raise the package size budget and stop hiding sandbox build failures

The docker sandbox build in `integration_docker` has been failing every
release since 0.22.3-release-7df5ac68. The build itself is fine; what fails is
`npm run prepare:package` inside the Dockerfile's builder stage:

    Error: Prepared package unpacked size 101897869 bytes exceeds 100663296 bytes

`assertPreparedPackageSize`'s 96 MiB budget was set in #6691 when the prepared
package was ~80.6 MiB. It has grown with ordinary feature work since —
96,802,417 bytes at 0.22.1, 98,582,313 at the 08-31 nightly, 101,897,869 now,
with the OpenTUI migration batches carrying two UI stacks at once — and it
crossed the ceiling. Raise the budget to 112 MiB, the same "headroom, not
target" call #6688 and #6691 made before it. vendor/ripgrep (24 MiB, five
platform binaries) and web-shell/assets (18 MiB, mostly shiki grammars,
mermaid and cytoscape) are the two places worth trimming if we would rather
bring the package down than keep raising the ceiling; that is a separate
change.

The reason a one-line size error cost a 20-minute release run to diagnose is
the other half of this commit:

- `build_sandbox.js` sent the image build's output to /dev/null unless VERBOSE
  was set, so the failure surfaced as an execSync stack trace with
  `stdout: null` and no way to tell what broke without rebuilding by hand.
  `e2e.yml` had already worked around this by setting VERBOSE; the release
  workflow had not. Now CI streams the output, and a quiet local build keeps
  it and prints the tail when the build fails.

- Nothing ran `prepare:package` before the publish job, so its guards were
  first exercised by the Dockerfile — the slowest consumer in the pipeline.
  `quality_build` now runs bundle + prepare:package after uploading its build
  artifact, which reports the same failure in minutes without touching what
  downstream jobs consume.

* fix(release): close sandbox output review gaps

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-02 11:38:33 +00:00
易良
7df5ac6898
fix(ci): isolate and bound release validation workloads (#10765)
* fix(ci): keep release validation off contended ECS hosts

Pin every release validation job to the hk4 host label, move the nightly
and preview crons out of the pool's peak hours and apart from each other,
give the web-shell Vitest project the same shared-ECS timeout ceiling as
cli/core, and retry timing-only workspace test failures twice.

Claude-Session: https://claude.ai/code/session_01AWWgJEqafyAT1Mc75T8N7h

* fix(ci): harden release validation isolation

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

* fix(ci): serialize release validation runs

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

* fix(ci): preserve manual release dispatches

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-02 04:58:08 +00:00
jinye
61697df9b0
feat(external-context): Publish the Mem0 Extension package (#10653)
* feat(external-context): publish Mem0 extension package

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

* test(release): Guard Mem0 manifest write-back

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

* fix(release): Normalize Mem0 manifest updates

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-01 22:12:00 +00:00
易良
d83576159b
perf(ci): shard release quality checks (#10619)
* perf(ci): shard release quality checks

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

* fix(ci): include generated templates in release artifact

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

* test(web-shell): wait for paginated loads in CI

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

* test(web-shell): allow slow boot imports in CI

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

* test(release): ratchet test-file discovery for every test:ci workspace

--passWithNoTests is required so a shard that receives no files can
exit 0, but it also lets a workspace that lost every test file pass in
all three shards while zero tests execute; the monolithic
test:release exited 1 in that case and blocked the release. Add a
contract test in the test:scripts lane (quality_scripts) that fails
when any test:ci workspace discovers no test file, so the release is
blocked again. Discovery mirrors vitest's default include, which every
workspace's test:ci config uses or narrows, so zero matches here means
zero discoverable tests under vitest.

Red probe: moving packages/channels/telegram's single test file out of
the tree fails the new test; restoring it goes green.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthe3hev7k

* fix(release): keep cancelled runs out of the quality failure gate

The quality aggregate used if: always(), so a cancelled release run
re-ran the result loop, read the component 'cancelled' results as
not-success, and exited 1 — turning quality.result into 'failure'.
That opens notify_failure's pre-existing needs.quality.result ==
'failure' gate, filing a "Release Failed" issue labeled
autofix/approved and dispatching autofix for a run an operator stopped
on purpose. Before this PR the monolithic quality job carried no
always(), so cancelled runs left quality.result='cancelled' and stayed
silent.

Use !cancelled() instead: cancelled runs skip the aggregate
(result 'skipped', still non-success, so publish stays fail-closed),
while any failed component still runs the loop and fails the gate.
Pin the new gate and the notify_failure failure clause in the contract
test.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthe3hev7k

* refactor(release): reuse release_test_env anchor in integration jobs

This PR introduced &release_test_env for the five quality jobs but
left integration_none and integration_docker with literal copies of
the same three OPENAI_* variables. Reuse the anchor so the seven
release-test jobs share one env definition; parsed output is
byte-identical for every job.

Also record the PR's final release.yml size (59855 bytes) in
.size-baseline: the sharding rewrite grew the file 1977 bytes past the
recorded 57878, inside the 4096 allowance but past the ratchet, which
exists to be bumped in the same PR as real growth.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthe3hev7k

* fix(release): prune node_modules when packing build outputs

The Pack Build Outputs find matched every directory named dist under
packages/integrations, so npm ci's nested dependency dist dirs (72 in
the current tree, e.g. packages/cli/node_modules/markdown-it/dist)
were tarred into release-quality-build and re-downloaded/extracted by
all five consumer jobs. Prune node_modules before matching dist, and
pin the prune in the release-workflow contract test.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthmo4kw80

* refactor(release-test): reuse getWorkspacePackageJsonPaths in shard test

The 'fans workspace tests into three complete Vitest shards' test
hand-rolled workspace-glob expansion (plus its own !-exclusion set),
duplicating the tested shared helper in scripts/workspaces.js that the
zero-test ratchet already uses. Swap in getWorkspacePackageJsonPaths so
both contract tests validate one implementation; behavior is unchanged
(both implementations enumerate the same 26 workspaces at this ref).

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthmo4kw80

* refactor(release-test): extract shared test:ci workspace selection helper

The shard-completeness pin and the zero-test ratchet both selected their
workspace sets through byte-identical blocks (read root package.json,
getWorkspacePackageJsonPaths, filter on test:ci). Extract the selection
once at describe scope so both release-gating ratchets keep gating on
the same workspace list.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtht3lw98c

* test(release): pin vitest as the last test:ci command in shard lanes

test:release:workspaces appends --shard/--passWithNoTests to each
workspace's test:ci body, so the flags only reach vitest when the
vitest invocation is the last command in the chain; a trailing command
would receive them while vitest ran the full suite in all three
shards. Assert the last command starts with `vitest run`; commands
before it (e.g. sdk-typescript's typecheck:public-surface) stay
accepted.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtht3lw98c

* test(release): pin needs edge, five-result mapping, and per-job skip gate

R1-17: assert every checkout consumer declares prepare in needs, so the
pinned ref expression cannot silently un-resolve to the event ref.
R1-4: assert the aggregate's env is the exact five-result mapping and the
verify loop references all five *_RESULT vars, so dropping or remapping an
entry fails here instead of publishing over a failed component.
R1-6: assert each of the five component quality jobs keeps its own
force_skip_tests gate, not just the aggregate.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthxdxgy8k

* fix(release): log packed outputs and fail closed on silent under-pack

Pack Build Outputs now prints every path it packs and refuses to tar when
find contributes nothing beyond the two hardcoded paths. A dropped `-o`
would turn the two -prune clauses into one conjunction matching nothing;
before this guard that packed an incomplete artifact with an empty log,
moving the symptom to downstream consumers as missing-dist errors. Pin the
guard and print in the pack-step contract test.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthxdxgy8k

* fix(release): pin consumer artifact contract, dist origin, and retention

R1-5: the build-once contract was pinned only on the producer side.
Assert every consumer job (typecheck, workspace shards, scripts) keeps
the quality_build needs edge, downloads release-quality-build via
actions/download-artifact to the runner.temp path, and unpacks it with
the pinned tar invocation after the download.
R1-10: the repo-root dist that Pack Build Outputs ships only exists as
a side effect of check:serve-fast-path-bundle (the check runs the
esbuild bundle with outdir dist; scripts/build.js never writes it).
Document the dependency with a step-level comment and pin the ordering
plus the documented contract in the workflow test.
R1-11: retention-days: 1 broke "Re-run failed jobs" more than a day
later, where the succeeded producer is not re-run and consumers must
still download its artifact. Raise retention to three days, update the
design doc re-run story, and pin the value next to the other upload
contract pins.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmthzj3978p

* fix(release): skip quality aggregate when prepare never ran

The aggregate's !cancelled() gate overrides the implicit needs success()
check, so on forks — where prepare is repository-gated and the five
component lanes follow it into skipped — the aggregate would still run,
see five non-success results, and exit 1. That turns a benign
all-skipped fork dispatch into quality=failure and can open
notify_failure's "Release Failed" issue.

Gate the aggregate on needs.prepare.result == 'success' (prepare is now
declared in its needs list). Same-repo releases keep the existing
fail-closed aggregation; fork runs stay fully skipped as on main.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmtiej7n89k

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-01 13:11:43 +00:00
易良
a0096eb5d6
fix(ci): make shared ECS Vitest concurrency tunable (#10667)
* fix(ci): cap Vitest workers per ECS runner

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

* fix(release): cap Vitest workers on shared ECS

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

* fix(ci): make ECS Vitest worker cap tunable

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-01 07:00:25 +00:00
顾盼
8290c81eea
feat(qwen-live): standalone voice daemon package — M1 + M2 (#10367)
* feat(qwen-live): scaffold standalone live daemon package with ported host stack

New workspace package @qwen-code/qwen-live (M1+M2 of the Live split,
issue #10118). This commit adds the package skeleton and the verbatim
ports that need no behavioral changes:

- host/: Live Host protocol v6 types, LiveHostCoordinator, and the
  discovery-file writer, ported from packages/cli/src/serve/live/
  together with their test suites (51 tests green)
- realtime/socket.ts, realtime/sanitize.ts: the two helper symbols the
  realtime client depends on, extracted from cli ui utils
- registered in scripts/build.js buildOrder and root vitest projects

* feat(qwen-live): generalize the realtime client tool surface

Port qwen-realtime-session.ts and adapt it for the orchestration model:
tools and instructions come from config, every declared tool dispatches
through onFunctionCall with the raw argument payload, and outputs are
receipt-style via submitFunctionOutput. The single-handoff machinery
(multiple_handoffs error, steering acknowledgement, open-call complete)
is removed; transport, response arbitration, pendingCalls timing,
barge-in, and defensive validation are unchanged from the port.
capturesTranscript marks handoff-style tools that capture the live
transcript tail at call time.

Test suite adapted: 27 tests green.

* feat(qwen-live): add the BackendAdaptor seam and the qwen serve adaptor

BackendAdaptor is the capability-bit-driven port between the live
orchestrator and a coding-agent backend. QwenCodeAdaptor drives a
running qwen serve daemon through the @qwen-code/sdk DaemonClient:
202-admission prompts, SSE event normalization (chunks and tool calls
live under session_update.data.update), reject-if-idle mid-turn
steering with a plain-prompt fallback, nested permission votes with
voter attribution, and attachment-backed image blocks. 23 unit tests
against an injected fake client.

* feat(qwen-live): orchestrator, tools, injector, permissions, and daemon entry

The M1+M2 core:
- seven receipt-style dispatch tools plus remain_silent, with a
  dispatcher that answers every call (timeout fallback included) and
  session_N/job_N/asset_N handle registries
- LiveSession wires Host audio, the realtime connection, tool handlers
  (appshot via the Host capture channel, handoff with voice-context
  packing and image attachments, busy-session steering), backend event
  pumps, and a bounded graceful-stop drain
- Injector: safe-insertion window (VAD + response-in-flight + playback
  estimate at 48 kB/s with a quiet gap), batch aggregation, per-job
  progress throttling, barge-in drops progress, spoken/detail split
- PermissionBroker: allow_always stays a one-shot protocol vote backed
  by a local TTL rule; remote resolutions retract queued asks
- LiveDaemon: Host WS endpoint (Bearer + Origin-reject), discovery
  publish with fail-fast mutual exclusion and dead-owner handoff,
  appshot channel marked ready (the in-process capture path needs no
  separate verification), SIGINT/SIGTERM shutdown
- SessionLog: JSONL with synchronous rotation (rename-before-reopen)

116 new unit tests; package total 167 green.

* feat(qwen-live): end-to-end test suites against a real qwen serve daemon

Test stack: a real qwen serve (spawned CLI bundle, fake-openai model
endpoint), a scriptable fake DashScope realtime WebSocket server, the
qwen-live daemon as a child process, and a protocol-v6 FakeHost client
— every boundary is the real wire.

Scenarios (11 tests): discovery publish/remove and hello/welcome,
toggle brings up the realtime session (tool schema + instructions
asserted on session.update), two-way audio byte fidelity, the full
handoff loop against real serve with [COMPLETE] injection and spoken
summary, injection-window hold/batch semantics, permission ask/vote
relay, and busy-session steering tiers.

The suites caught a real bug the unit layer could not: per-session
calls must echo the daemon-issued clientId from the create/attach
response (a self-made id is rejected by the client registration guard
and broke handoff admission and vote attribution). QwenCodeAdaptor now
tracks and echoes the issued id per session.

* chore(release): keep @qwen-code/qwen-live versioned independently

Same treatment as sdk/mobile-mcp/node-repl: the incubating package
stays at its own 0.x version instead of lock-stepping with the CLI
release train. The verbatim guard in package-scripts.test.js is
updated alongside.

* fix(qwen-live): harden review findings across dispatcher, injector, and permission relay

Self-review fixes bundled after the adversarial pass:

- adaptor: classify snake_case permission option ids (proceed_once) —
  the word-boundary regex never matched through "_", so an allow vote
  silently degraded to a cancelled outcome and serve dropped the turn
- adaptor: detect the pending-prompt limit by error name as well as
  status, and fall image-bearing steers back to a regular prompt
- dispatcher: a timed-out tool now answers with a pending receipt that
  tells the model not to retry (the handler keeps running)
- injector: permission items flush ahead of the size-capped context
  join; spoken lines merge whole-line greedily under the 280-char cap
- live-session: resubscribe the event pump with backoff until
  session_closed; a rejected permission chain can no longer become an
  unhandled rejection; receipts are clamped and never empty
- permission broker: a failed auto-approval falls back to the spoken
  ask instead of losing the request
- daemon/config/index: appshot readiness is declared at construction,
  numeric config ports parse, stray rejections are logged not fatal
- e2e harness: QWEN_LIVE_E2E_KEEP=1 keeps the temp dirs for post-mortem

* fix(qwen-live): address review round 1 — permission voting, shutdown, discovery reclaim, and hardening

Permission trust boundary:
- classify options by the wire's structured kind enum (allow_once/
  allow_always/reject_*) instead of word regexes over snake_case ids,
  and vote the least-escalating match — a spoken "allow" now lands on
  proceed_once, never proceed_always_project
- respond_permission's note is no longer discarded: it is logged and
  relayed to the backend session as a follow-up user instruction
- track own votes per session so byUs attribution works on adopted
  sessions; narrow allow_always standing rules to the exact normalized
  command instead of a two-token prefix

Lifecycle:
- daemon stop() no longer hangs when SIGINT lands before listen() or
  when a Host redials during shutdown (stopping gate + client terminate)
- dead-owner discovery reclaim now follows the cli contract (no-op
  commitOwner during handoff, republish after) — previously it could
  never succeed and a crash left the daemon unable to start
- stop drain settles on the input-commit ack instead of burning the
  full 30s budget; response.created during the drain no longer flips
  the call back to speaking; a host-initiated start can no longer race
  deactivate()

Robustness:
- a late ASR final for an already-consumed input item is ignored as
  stale_input instead of dropping the call
- raise the Host frame cap to cover a conformant CJK accessibility
  dump (64KiB frame cap contradicted the 32k-char field cap)
- bin entry works through npm's bin symlink (realpath + pathToFileURL)
- steered handoffs that join a running turn reuse the existing job
  instead of orphaning it; over-long steers fall back to a queued
  prompt; session_list unions created cwds, requests a full page, and
  maps hasActivePrompt for untracked sessions; created-session labels
  are applied via session metadata
- injector: jobless throttle keys use the full context, dedup is
  symmetric, and barge-in drops clear the throttle stamp; sentence
  shapers handle CJK punctuation and mid-token periods
- config loader: ENOENT-only swallow, BOM strip, ~ expansion, typed
  port validation, source-accurate errors; session log close() is
  terminal; transcript retention is capped

Tests and docs: new config/daemon/sanitize suites, win32 guards
restored on discovery mode assertions, protocol parity test now checks
both type copies, qwen-live e2e suites join the no-AK PR gate, fake
model gating in the inject e2e matches only the current turn and is
consumed on match, FakeHost answers capture requests, lockfile diff
reduced to qwen-live-only entries, README states the installer gap.

* test(qwen-live): pin injector size caps and steered-message delivery

- injector: 6000-char combined-context cap, whole-line spoken join at
  the 280-char boundary, single-line ellipsis truncation
- steering e2e: assert the joined instruction actually reached the
  model endpoint, not just the receipt wording

* fix(sdk): raise daemon browser bundle budget

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

* feat(qwen-live): scaffold standalone live daemon package with ported host stack

New workspace package @qwen-code/qwen-live (M1+M2 of the Live split,
issue #10118). This commit adds the package skeleton and the verbatim
ports that need no behavioral changes:

- host/: Live Host protocol v6 types, LiveHostCoordinator, and the
  discovery-file writer, ported from packages/cli/src/serve/live/
  together with their test suites (51 tests green)
- realtime/socket.ts, realtime/sanitize.ts: the two helper symbols the
  realtime client depends on, extracted from cli ui utils
- registered in scripts/build.js buildOrder and root vitest projects

* feat(qwen-live): generalize the realtime client tool surface

Port qwen-realtime-session.ts and adapt it for the orchestration model:
tools and instructions come from config, every declared tool dispatches
through onFunctionCall with the raw argument payload, and outputs are
receipt-style via submitFunctionOutput. The single-handoff machinery
(multiple_handoffs error, steering acknowledgement, open-call complete)
is removed; transport, response arbitration, pendingCalls timing,
barge-in, and defensive validation are unchanged from the port.
capturesTranscript marks handoff-style tools that capture the live
transcript tail at call time.

Test suite adapted: 27 tests green.

* feat(qwen-live): add the BackendAdaptor seam and the qwen serve adaptor

BackendAdaptor is the capability-bit-driven port between the live
orchestrator and a coding-agent backend. QwenCodeAdaptor drives a
running qwen serve daemon through the @qwen-code/sdk DaemonClient:
202-admission prompts, SSE event normalization (chunks and tool calls
live under session_update.data.update), reject-if-idle mid-turn
steering with a plain-prompt fallback, nested permission votes with
voter attribution, and attachment-backed image blocks. 23 unit tests
against an injected fake client.

* feat(qwen-live): orchestrator, tools, injector, permissions, and daemon entry

The M1+M2 core:
- seven receipt-style dispatch tools plus remain_silent, with a
  dispatcher that answers every call (timeout fallback included) and
  session_N/job_N/asset_N handle registries
- LiveSession wires Host audio, the realtime connection, tool handlers
  (appshot via the Host capture channel, handoff with voice-context
  packing and image attachments, busy-session steering), backend event
  pumps, and a bounded graceful-stop drain
- Injector: safe-insertion window (VAD + response-in-flight + playback
  estimate at 48 kB/s with a quiet gap), batch aggregation, per-job
  progress throttling, barge-in drops progress, spoken/detail split
- PermissionBroker: allow_always stays a one-shot protocol vote backed
  by a local TTL rule; remote resolutions retract queued asks
- LiveDaemon: Host WS endpoint (Bearer + Origin-reject), discovery
  publish with fail-fast mutual exclusion and dead-owner handoff,
  appshot channel marked ready (the in-process capture path needs no
  separate verification), SIGINT/SIGTERM shutdown
- SessionLog: JSONL with synchronous rotation (rename-before-reopen)

116 new unit tests; package total 167 green.

* feat(qwen-live): end-to-end test suites against a real qwen serve daemon

Test stack: a real qwen serve (spawned CLI bundle, fake-openai model
endpoint), a scriptable fake DashScope realtime WebSocket server, the
qwen-live daemon as a child process, and a protocol-v6 FakeHost client
— every boundary is the real wire.

Scenarios (11 tests): discovery publish/remove and hello/welcome,
toggle brings up the realtime session (tool schema + instructions
asserted on session.update), two-way audio byte fidelity, the full
handoff loop against real serve with [COMPLETE] injection and spoken
summary, injection-window hold/batch semantics, permission ask/vote
relay, and busy-session steering tiers.

The suites caught a real bug the unit layer could not: per-session
calls must echo the daemon-issued clientId from the create/attach
response (a self-made id is rejected by the client registration guard
and broke handoff admission and vote attribution). QwenCodeAdaptor now
tracks and echoes the issued id per session.

* chore(release): keep @qwen-code/qwen-live versioned independently

Same treatment as sdk/mobile-mcp/node-repl: the incubating package
stays at its own 0.x version instead of lock-stepping with the CLI
release train. The verbatim guard in package-scripts.test.js is
updated alongside.

* fix(qwen-live): harden review findings across dispatcher, injector, and permission relay

Self-review fixes bundled after the adversarial pass:

- adaptor: classify snake_case permission option ids (proceed_once) —
  the word-boundary regex never matched through "_", so an allow vote
  silently degraded to a cancelled outcome and serve dropped the turn
- adaptor: detect the pending-prompt limit by error name as well as
  status, and fall image-bearing steers back to a regular prompt
- dispatcher: a timed-out tool now answers with a pending receipt that
  tells the model not to retry (the handler keeps running)
- injector: permission items flush ahead of the size-capped context
  join; spoken lines merge whole-line greedily under the 280-char cap
- live-session: resubscribe the event pump with backoff until
  session_closed; a rejected permission chain can no longer become an
  unhandled rejection; receipts are clamped and never empty
- permission broker: a failed auto-approval falls back to the spoken
  ask instead of losing the request
- daemon/config/index: appshot readiness is declared at construction,
  numeric config ports parse, stray rejections are logged not fatal
- e2e harness: QWEN_LIVE_E2E_KEEP=1 keeps the temp dirs for post-mortem

* fix(qwen-live): address review round 1 — permission voting, shutdown, discovery reclaim, and hardening

Permission trust boundary:
- classify options by the wire's structured kind enum (allow_once/
  allow_always/reject_*) instead of word regexes over snake_case ids,
  and vote the least-escalating match — a spoken "allow" now lands on
  proceed_once, never proceed_always_project
- respond_permission's note is no longer discarded: it is logged and
  relayed to the backend session as a follow-up user instruction
- track own votes per session so byUs attribution works on adopted
  sessions; narrow allow_always standing rules to the exact normalized
  command instead of a two-token prefix

Lifecycle:
- daemon stop() no longer hangs when SIGINT lands before listen() or
  when a Host redials during shutdown (stopping gate + client terminate)
- dead-owner discovery reclaim now follows the cli contract (no-op
  commitOwner during handoff, republish after) — previously it could
  never succeed and a crash left the daemon unable to start
- stop drain settles on the input-commit ack instead of burning the
  full 30s budget; response.created during the drain no longer flips
  the call back to speaking; a host-initiated start can no longer race
  deactivate()

Robustness:
- a late ASR final for an already-consumed input item is ignored as
  stale_input instead of dropping the call
- raise the Host frame cap to cover a conformant CJK accessibility
  dump (64KiB frame cap contradicted the 32k-char field cap)
- bin entry works through npm's bin symlink (realpath + pathToFileURL)
- steered handoffs that join a running turn reuse the existing job
  instead of orphaning it; over-long steers fall back to a queued
  prompt; session_list unions created cwds, requests a full page, and
  maps hasActivePrompt for untracked sessions; created-session labels
  are applied via session metadata
- injector: jobless throttle keys use the full context, dedup is
  symmetric, and barge-in drops clear the throttle stamp; sentence
  shapers handle CJK punctuation and mid-token periods
- config loader: ENOENT-only swallow, BOM strip, ~ expansion, typed
  port validation, source-accurate errors; session log close() is
  terminal; transcript retention is capped

Tests and docs: new config/daemon/sanitize suites, win32 guards
restored on discovery mode assertions, protocol parity test now checks
both type copies, qwen-live e2e suites join the no-AK PR gate, fake
model gating in the inject e2e matches only the current turn and is
consumed on match, FakeHost answers capture requests, lockfile diff
reduced to qwen-live-only entries, README states the installer gap.

* test(qwen-live): pin injector size caps and steered-message delivery

- injector: 6000-char combined-context cap, whole-line spoken join at
  the 280-char boundary, single-line ellipsis truncation
- steering e2e: assert the joined instruction actually reached the
  model endpoint, not just the receipt wording

* feat(qwen-live): port the Live Host installer — M1 gap closed

`live-host-installer.ts` moves verbatim from
packages/cli/src/serve/live/ (same OSS/GitHub dual-source manifest
fetch, sha256 + codesign + team-identifier verification, staging +
backup install with rollback, and the retryable status machine), with
its 11-test suite. The daemon gains the matching HTTP surface:

- GET /live/setup — installed Host state
- POST /live/setup/install — download + verify + install the latest
  POST /live/setup/launch — open the installed app

All three sit behind the daemon's existing Bearer wall (a browser
context sending Origin is always refused, wrong token 401) — pinned by
a new daemon test; unknown paths stay 404 and GET /healthz answers
plain 200. Bootstrap no longer depends on the built-in qwen-code Live
integration having installed the Host first.

* fix(qwen-live): attach the appshot-timeout rejection handler before advancing fake timers

The timeout rejects synchronously inside advanceTimersByTimeAsync; a
handler attached only afterwards leaves an unhandled rejection for an
instant, which vitest counts as a run-level error (CI fails the
package with Errors: 1 even though all 250 tests pass). Attach the
expectation first, await it after the clock advances.

* test(qwen-live): sync the no-AK gate pin with the qwen-live suites

The no-ak-integration-ci pin (added upstream after our branch point)
asserts the gate's file list verbatim; add the four qwen-live suites
the gate has been running since they joined it.

* fix(qwen-live): address review round 2 — trust-boundary keys, backpressure, session lifecycle

Criticals (all probed and reproduced before fixing):

- broker titleKeyOf: split on the FIRST colon and keep the entire
  remainder (commands routinely contain colons — a truncated key
  auto-approved a different -m message), and stop case-folding
  (/a vs /A are different paths on Linux). The whitespace-collapse
  pin is preserved
- broker deny: an explicit spoken refusal now revokes the standing
  rule that matched the same request — the silent auto-answer's
  fallback path could otherwise be overridden by the invisible rule
  on the next identical request
- adaptor activeJobRef: a busy-session fallback (image steer, over-
  long text 400) no longer overwrites the running turn's ref with the
  queued prompt's id — later steer receipts report the right job
- adaptor byUs attribution: the daemon's originator stamp is now
  authoritative when present; ownVotes only covers anonymous
  resolutions. Another client resolving during our vote round-trip no
  longer reads as our own answer (R1-2 regression, fixed test-first)
- adaptor permission titles: control sequences stripped before the
  title reaches speech or keys a standing rule (whole command kept —
  no first-line cut, unlike the progress path)
- live-session pushAudio: the provider's backpressure signal is
  propagated (the port source fails the call) and onAudioDropped is
  wired to failCall — a socket over its buffer cap no longer silently
  garbles the utterance; the deliberate true for stale/connecting
  frames is untouched
- handles: closed sessions stop resolving entirely (registry-level),
  and session_closed clears the call's default handle so a session-
  less handoff rebuilds instead of retrying a dead target forever
- handles: read-site reconciliation — session_list and session_monitor
  transition a session's stale non-terminal jobs to done when the
  backend itself reports idle (a turn_complete emitted while no pump
  was subscribed no longer reports a running active_job forever)

Suggestions on this PR's new surfaces:

- live-host.yml: packages/qwen-live/src/host/** joins the path filter
  so the parity-test lane runs on qwen-live-only PRs instead of first
  failing in the merge queue
- installer: the install route passes force=true (the recovery path
  for a corrupted-but-present app, matching the CLI mirror), and
  launch() gains the non-darwin guard in BOTH copies so the byte
  parity pin stays green
- index: directory-form invocation (node packages/qwen-live) now
  runs main() instead of exiting 0 silently
- realtime: the dead lastHandoffEntryCount machinery is removed
  (truncate-and-reset replaced the cursor it pretended to track)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: 易良 <1204183885@qq.com>
Co-authored-by: yiliang114 <effortyiliang@gmail.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-09-01 06:37:23 +00:00
Gergely Imreh
265e7f1fbe
fix(scripts): remove never actually supported npm "--parallel" flag (#8725)
Some checks failed
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / Integration Tests (no-AK, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Blocked by required conditions
Qwen Code CI / Desktop Shell (ubuntu-22.04) (push) Blocked by required conditions
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Qwen Code CI / Desktop Shell (windows-2022) (push) Blocked by required conditions
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
SDK Java / windows-latest / Java 21 (push) Waiting to run
SDK Java / Real daemon E2E / Java 11 (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
npm cache producer / Save npm cache (push) Has been cancelled
SDK Python / Classify PR (push) Has been cancelled
SDK Python / SDK Python (3.10) (push) Has been cancelled
SDK Python / SDK Python (3.11) (push) Has been cancelled
SDK Python / SDK Python (3.12) (push) Has been cancelled
The `--parallel` flag was a proposal for `npm`, but it wasn't
merged in the end. With version 12 unknown flags are now
errors instead of warnings, so on that version it blocks running
`npm run test` and similar tasks.
2026-08-29 18:20:56 +00:00
易良
b22471c4e7
chore(ci): migrate CUA npm packages to trusted publishing (#9836)
* chore(ci): remove stale NPM_TOKEN references from cua-sdk publish

* test(scripts): pin cua sdk trusted publishing workflow

* test(scripts): pin cua sdk publish auth contract

* chore(ci): migrate node repl publish to trusted publishing
2026-08-26 09:29:22 +00:00
qqqys
b449a9536a
feat(channels): add DingTalk Workspace channel (#9394)
* feat(channels): add DingTalk Workspace channel

Add a DingTalk Workspace (DWS) channel package so a workspace can be
driven from DingTalk alongside the existing channels.

- packages/channels/dws: new workspace holding the DWS client, event
  stream, environment resolution and channel implementation, with the
  event-source fixtures used by its tests.
- cli: register DWS in the channel registry and its builtin list.
- web-shell: recognise the DWS platform in the channels UI.
- docs: document the channel and its configuration under
  docs/users/features/channels.
- build/release: include the new workspace in the build, clean and
  release-version scripts and the vitest project list.

The channel watches native DingTalk todos, routes document and todo
replies back to their originating conversation, bounds notification
retries, and keeps sender identity authoritative for direct messages.

* fix(dws): classify spawn-resource errnos as not sent, and test against base's source

Round 1 review, two Critical findings.

vitest.config.ts — the new package's config was the only channel config
without the `@qwen-code/channel-base` → source alias its five siblings carry,
so `cd packages/channels/dws && npx vitest run` (the workflow AGENTS.md
prescribes) depended on a prior `tsc --build` of base. Reproduced the
reviewer's witness in this worktree with base/dist moved aside: without the
alias vitest dies in `packageEntryFailure` and runs zero tests; with it,
63/63 pass. Even when dist exists it may lag base's source — it did here, by
four days.

dws-client.ts — `DWS_NOT_SENT_ERROR_CODES` listed only the path errnos, so a
`dws` process that never started because of fd or memory exhaustion
(`EMFILE`/`ENFILE`/`ENOMEM`/`EAGAIN` and family) was classified `unknown`.
The todo and document reply paths in dws-channel.ts swallow `unknown` as
"the originating task will not be rerun", so a user's final reply was dropped
permanently on one log line instead of being retried — and the retry is safe,
since the fingerprint is not persisted when delivery fails. The set now
carries the whole `uv_spawn` pre-exec family. Everything else the callback
reports — a non-zero exit (numeric `code`), a timeout kill (`code === null`),
`ABORT_ERR`, a `maxBuffer` overrun — happened with a child already running
and stays `unknown`, because a retry there could duplicate a delivery.

The classification moved into an exported `classifyDwsCommandFailure` so the
table can be driven directly: the resource errnos need real fd or memory
exhaustion to reproduce through a spawn, which no unit test can stage safely.
The existing missing-executable test still covers the wiring end to end.

Verified: packages/channels/dws — 191 passed (5 files). Mutation-verified:
reverting the errno set turns exactly the 12 added codes red (12 failed /
51 passed); dropping the vitest alias with base/dist absent turns the suite
from 63 passed into a collection failure. eslint and prettier clean. The one
tsc error on this branch (`displayText` missing from `Envelope`) is worktree
build skew — base/dist was built 2026-08-10, base/src changed 2026-08-14, and
the field is present in the source; it reproduces identically with these
changes stashed.

* fix(dws): stop a denied sender from consuming a document comment's dedup slot

Round-2 review, R2-4 (Critical).

`notificationKey` is `documentNotificationKey(documentId, commentKey)` — no
sender in it — so a `'denied'` outcome falling into the `else` branch marked
that (document, comment) pair processed for good. Every later notification for
the same comment, live or polled, then hit
`processedMessages.includes(notificationKey)` and returned silently, including
one from a sender who IS allowed. The cursor persists, so the drop survived
restarts.

Concretely, with `senderPolicy: 'allowlist'` and `allowedUsers: ['open-bob']`:
Alice (not allowlisted) @-mentions the bot in a document comment and is denied;
Bob then mentions the bot on the same comment thread — the ordinary
multi-reviewer document flow — and is dropped forever, with no dispatch, no
pairing and no log.

A denied notification is now parked with `rememberPendingDocumentNotification`
like a `'pairing'` one rather than consuming the slot. Replay already skips a
pending entry whose sender fails `gate.isAllowed`, so a denied sender does not
get retried in; and an allowed sender reaching the same comment clears the
entry on the way through.

The existing `applies sender access policy to document mention notifications`
cannot cover this — its denied and allowed notifications are on DIFFERENT
comments, so the shared key is never exercised. New test puts both on the same
comment. Mutation-checked: restoring the old condition reddens it with
`bridge.prompt` called 0 times against an expected 1, reproducing the review's
own witness.

Verification: `npm run build` and `tsc --noEmit` clean in packages/channels/dws;
eslint clean on both changed files; full package suite 192/192 (118 in
dws-channel.test.ts, 1 new).

* fix(dws): stop a poison message, a full pending queue, and an unreachable
replay from pinning the watermark (R2-1, R2-2, R2-4 queue)

Three ways history polling could stall forever, each measured:

**R2-2, poison message.** A message whose turn threw was never marked
processed, so the watermark never advanced and every poll re-ran it as a
full agent turn — one model call per iteration, no cap, no backoff —
while the pinned watermark grew the query window without bound and the
throw starved every newer message behind it. Pending-document replay
already had retry accounting; this path had none. Inbound failures are
now counted per message and persisted in the cursor: under budget the
error still propagates (redelivery retry and the concurrent-duplicate
contract depend on that, and their tests pin it), and once the budget is
spent the message is marked processed and dropped with a logged reason.

**Pending-queue cap.** `rememberPendingDocumentNotification` threw at
MAX_PROCESSED_ITEMS, and the throw aborted the direct-message loop
before the checkpoint, the watermark and `markProcessedMessage` — so
every later poll re-scanned a growing window and re-threw on the same
never-marked message, surviving restarts in the cursor. The queue's only
drain is an allowed sender later processing the same comment, so entries
parked for unapproved senders never leave: one unpaired member
@-mentioning the bot in 5,000 distinct comments broke document history
polling until manual cursor surgery. It now evicts the oldest instead,
which costs at most a pairing prompt nobody approved.

**R2-1, the replay the fixture could not recover.** The test fake
ignored its `startTime`/`endTime`, so it certified a recovery the
production arithmetic cannot perform. Fixed on both sides: the fake now
filters by its window like the real client (and `message()` defaults
`eventTime` to now, since real messages always carry one — six fixtures
were silently relying on epoch 0), and the stale-replay guard now pulls
`notificationWatermark` back to the parked notification's event time. It
parks document notifications UNMARKED on purpose, "for polling to
recover"; on a fresh cursor the watermark started at
`connectionStartedAt` and the window opened at `watermark − 5s` —
exactly the guard's own drop boundary — so everything it parked was
strictly outside every window that watermark would ever produce.

Every fix is mutation-verified: reverting the retry budget re-runs the
poison turn once per poll (8 polls, 8 turns), restoring the queue throw
reproduces the reviewer's stderr and the pinned watermark, and dropping
the watermark pull-back leaves the replayed notification unrecovered.
Suite 194/194 green; `tsc -p packages/channels/dws` clean.

R1-2 (self-identity degradation) is not in this commit — both fixes the
review proposes collide with behaviour this suite pins deliberately; see
the thread.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(dws): budget every inbound surface, and stop restarting a dead source (R4-1, R4-3)

R4-1: round 3 added an inbound failure budget, but wired it into one of the
three `handleInbound` call sites — the mention/live-IM path. The other two kept
the exact unbounded-retry mode the budget's own doc comment says it exists to
close.

- Document notifications (`processDocumentNotification`): a throw escapes
  `pollOnce`'s sorted loop and is swallowed by the outer catch, so nothing is
  marked processed and `notificationCheckpoint`/`notificationWatermark` — both
  assigned after the loop — never advance. Every 5s poll re-ran the same full
  agent turn, forever, starving every newer notification behind it.
- Native todos (`pollTodos`): the fingerprint is remembered only on success, so
  a todo whose turn keeps throwing was re-fetched and re-run every poll,
  forever.

`recordInboundFailure` now takes the drop action as a parameter, because "stop
re-running this" differs per surface: marking the key processed is right for a
message, a document notification carries its own `notificationKey` (and a
pending entry to clear), and a todo is re-fetched by fingerprint. The default
keeps the mention path byte-identical.

R4-3: `retryable: false` is terminal before ready — `retryLimit` returns 0 —
but `scheduleImRestart` never consulted it, and `startImSource` resets
`restartAttempts` to 0 every time a subscription becomes ready. The backoff
exponent therefore stayed at 0, so a permanently denied consumer (permission
revoked, subscription not allowed) was respawned at a constant ~3s forever —
one `dws event consume` child every 2-3s per affected source — while the
channel reported itself connected and delivered nothing for that source.
Post-ready now matches pre-ready: terminal, with a log line saying so.

Verification (`cd packages/channels/dws`):
- `npx vitest run` — 197 passed (was 194; three new tests).
- `npx tsc -p tsconfig.json --noEmit` — clean.
- Mutation checks, one per fix, each turning exactly its own test red and
  leaving the other 122 green:
  - drop the `retryable === false` guard -> `stops restarting a source that
    died permanently after becoming ready` fails.
  - drop the document-path budget -> `drops a document notification whose turn
    keeps failing, and stops starving newer ones` fails (the newer
    notification is never reached).
  - drop the todo-path budget -> `drops a native todo whose turn keeps
    failing` fails (8 turns instead of 5).
- eslint + prettier clean.

Not addressed in this commit: R4-2 (checkpoint drain overwriting the stale
replay pull-back), R4-4, R1-2, and R4-5..R4-8.

* fix(dws): stop an in-flight poll from clobbering the stale-replay pullback (R4-4)

`handleImMessage` leaves a replayed document notification UNMARKED on purpose,
for history polling to pick up, and pulls `notificationWatermark` back to the
replay's `eventTime` so a future window can reach it. `pollOnce` then wrote
`checkpoint.endTime` over that watermark unconditionally when its own window
finished — and `checkpoint.endTime` is always past the replay's `eventTime`.

The race is not hairline: `runLoop` polls immediately on connect and the IM
subscriptions start before the poll loop, so a startup replay arrives precisely
while poll #1's `listDirectMessages` is awaiting. One clobber puts the parked
replay outside every window the watermark will ever produce — no turn, no log,
no error, and it survives restarts because `saveCursor()` persists it.

`pollOnce` now records whether the watermark was pulled back while its
direct-message fetch was in flight, and on that path drops the window instead of
finishing it: neither the advance nor the paginated checkpoint resume is safe,
because the checkpoint was itself derived from the pre-pullback watermark. The
next poll re-derives a window from the pulled-back value.

Test: `keeps the stale-replay pullback when a poll was already in flight` emits
the replay from inside `listDirectMessages`. Mutation-checked — forcing the
guard false reddens it with `inbound` empty, matching the reviewer's witness
(`dispatched = 0`). It also asserts the second query window opens at or before
the replay's `eventTime`, so a fake that ignored its window could not certify it.

* fix(dws): stop three silent, permanent losses of a document mention (R6-1/R6-2/R6-3)

All three Criticals round 6 raised share a failure shape: a document comment is
consumed by something that had no right to consume it, the user gets no reply,
and nothing is logged. Each is fixed at the point that consumes the slot.

R6-1 — `handleImMessage` pullback (dws-channel.ts): R4-4 rescued a stale replay
by pulling the notification watermark back, but the flag `pollOnce` consults is
cleared at the top of every fetch, so it only ever covered a replay that landed
DURING one. A pullback arriving in the gap between two polls is reset before it
is read; a persisted multi-page `notificationCheckpoint` then resumes a window
that starts after the replay and finishes by writing `checkpoint.endTime` back
over the pulled-back watermark. The replay was left unmarked on purpose, so
after that no window ever reaches it again. The pullback branch now drops the
checkpoint as well, which makes the rescue durable regardless of when the
replay arrived; the in-flight flag still guards the during-a-fetch case.

R6-2 — in-flight awaiter (dws-channel.ts): a pending entry means the in-flight
turn PARKED the comment for a sender it would not serve, which says nothing
about the caller waiting behind it. Marking unconditionally consumed an ALLOWED
sender's mention outright — replay only re-drives a parked entry whose own
`senderId` passes the gate (the denied one never will), and the allowed
sender's marked message key is skipped by every later history poll. The awaiter
now marks only when the comment is genuinely processed, or when this caller is
no more entitled to it than the sender already parked. This is what the
denied-sender comment further down already claimed happened ("an allowed sender
reaching the same comment clears the entry on the way through") — the awaiter
was the path that never let them reach it.

R6-3 — failure-budget drop closure (dws-channel.ts): the closure marked the
sender-agnostic `notificationKey` (`document\0comment`, no sender), so five
failed turns — about 25s of transient model or bridge trouble, since each 5s
poll re-runs an unmarked notification — dropped every FUTURE mention of that
comment from anyone, permanently and across restarts. It now marks only the
failing message's own `key`, which is what stops the window re-running it, so
the R4-1 starvation this budget closes stays closed.

Tests (dws-channel.test.ts), each mutation-verified against the pre-fix code:
- `keeps a stale-replay pullback that arrives between two polls` — persists a
  bounded checkpoint, emits the replay with no poll in flight, asserts the
  checkpoint is released and the next window reaches back over the replay.
  Reverting R6-1: `expected { startTime: … } to be undefined`.
- `lets an allowed sender through while a denied turn on the same comment is in
  flight` — the concurrent counterpart to the existing R2-4 test, which lets
  the denied turn finish first and so cannot reach the awaiter. Reverting R6-2:
  the allowed sender's prompt is never called.
- `lets a later mention of a dropped comment retry with a fresh budget` — five
  failing polls, then a different reviewer on the same comment after the
  outage. Reverting R6-3: `expected [] to deeply equal [ ObjectContaining{…} ]`.

Verification: `npx vitest run` in packages/channels/dws — 201 passed (5 files);
`npx tsc --noEmit -p packages/channels/dws/tsconfig.json` clean; `npm run build`
in that package clean; eslint and prettier clean on both changed files.

R1-2 is untouched: it still needs a maintainer call on which pinned contract
gives, and is not something this commit should decide.

* fix(dws): resolve the sender gate before reading a mentioned document (R7-1)

`parseDocumentMentionNotification` reconstructs `(documentId, commentKey)`
from rendered message text, so a bare alidocs URL in an ordinary DM forges a
mention card the channel cannot tell apart from a genuine platform
notification. `processDocumentNotification` then called
`readDocumentContext` on that attacker-named document BEFORE `handleInbound`
resolved the sender gate, so under the documented default
`senderPolicy: 'pairing'` an unpaired stranger could force this profile to
perform an authenticated read of any document it can reach — a turn the
channel would never serve them.

Resolve `gate.isAllowed(message.senderId)` first and read only for a sender
this channel will actually answer. The envelope already carries a "Document
Markdown was unavailable" fallback, the `preflightInbound` document branch
still parks the mention exactly as before, and
`replayPendingDocumentNotifications` re-enters this path once the sender is
approved, so an approved turn still gets its document context — just after
the gate instead of before it.

BEHAVIOR FLIP: `replays a pairing-pending document mention after approval`
pinned `readDocument` being called once for the still-unpaired sender and
twice overall. That pinned expectation was the defect: it asserted an
authenticated read driven by a sender the gate had already refused. It now
expects zero reads before approval and one after. Verified by mutation —
reverting the guard turns both this test and the new forged-mention test red.

Still open on this class and NOT addressed here: the pairing-code write into
the attacker-named comment thread. Closing that needs either fail-closed
verification that `commentKey` is a real comment on `documentId` mentioning
this profile (no DWS CLI surface exposes it — `listMentionedMessages` covers
group IM, not document comments) or structured mention events, so it is a
maintainer contract call rather than a local fix.

Verification:
- packages/channels/dws: 202 passed (5 files), including the new
  `does not read a forged document mention before the sender gate resolves`
- tsc --noEmit -p packages/channels/dws/tsconfig.json: clean
- eslint + prettier --check on both changed files: clean

* fix(dws): list the dws channel as a cli test build prerequisite

`channel-registry.ts` dynamically imports `@qwen-code/channel-dws`, whose
package.json resolves the bare specifier to `dist/index.js` and which
`packages/cli/vitest.config.ts` does not alias to source. It therefore
belongs in `DIST_PREREQUISITES['packages/cli']` alongside every other
builtin channel, so a cli test run on an unbuilt checkout reports the
actionable "run npm run build" message instead of a raw resolution error.

This is what the required `Test (ubuntu-latest, Node 22.x)` check caught
on 4bf040766c: scripts/tests/vitest-global-setup.test.js asserts the list
stays in sync with the registry, and dws was the one registry import
missing from it.

Verified: `npx vitest run scripts/tests/vitest-global-setup.test.js`
29 passed; reverting this one line reproduces the CI assertion exactly
("missing prerequisite entry for packages/channels/dws"), 1 failed | 28
passed. prettier --check and eslint clean.

* fix(dws): close current review blockers

* test(dws): pin fail-closed self identity gate

* fix(dws): preserve retryable inbound work

* fix(dws): preserve in-flight catch-up mentions

* fix(dws): align channel-base on the workspace version so npm ci resolves

`Dependency CVE audit` has failed every run with:

    npm ci can only install packages when your package.json and
    package-lock.json are in sync.
    Missing: @qwen-code/channel-base@0.21.11 from lock file

The diagnosis of "stale base" was right, but the stale file is this PR's
own. `packages/channels/dws` was written when the workspace was at
0.21.11 and pins that version; every sibling channel — dingtalk, feishu,
github, gitlab, qqbot, telegram, wecom, weixin — now says 0.21.14, which
is what `packages/channels/base` actually publishes. A workspace package
cannot satisfy 0.21.11, so npm resolved `@qwen-code/channel-base` for dws
from the REGISTRY instead of linking the sibling, leaving a nested
`packages/channels/dws/node_modules/@qwen-code/channel-base` entry that
`npm ci` refuses. Merging current main cannot fix it: main is not where
the pin lives.

Bump dws to 0.21.14 for both its own version and its channel-base
dependency, matching every sibling, and regenerate the lockfile. The
nested registry entry is gone and dws now links the workspace like the
others. `npm ci --dry-run` completes, and dws typechecks and passes all
211 tests against the workspace channel-base rather than the published
0.21.11 it was resolving before.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VgTjRF91xANQh6SY9YGyCf

* fix(dws): replay failed direct messages, and close open review items

* fix(deps): bump tar to 7.5.22 to unblock CVE audit (#9394)

The 2026-08-21 advisory GHSA-r292-9mhp-454m flags tar <= 7.5.20 as
high severity, failing the Dependency CVE audit gate. Main already
moved to 7.5.22 in #9703, but that landed after this branch's last
merge of main. Bump the lockfile entry in-range (core/cli declare
^7.5.19) to match main, and regenerate the committed NOTICES.txt
artifact whose freshness is enforced by CI.

* fix(dws): unblock npm ci, add publish metadata, and keep todo fetch failures out of the turn budget (R13-1, R13-2, R14-1)

* fix(dws): dedup threaded pairing comments on a persisted marker instead of the rotating code (R15-1)

* fix(dws): clear the todo pairing marker when pairing resolves, not on turn success (R16-1)

* fix(dws): address current review blockers

* fix(dws): satisfy event fixture lint

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: qqqys <266654365+qqqys@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-25 06:40:47 +00:00
顾盼
37cedea5b2
feat(computer-use): replace built-in tools with bundled skill (#9856) 2026-08-24 11:05:23 +00:00
Shaojin Wen
2c64ebe980
feat(autofix): audit the approach instead of stopping on growth-budget breach (#9262)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* feat(autofix): audit the approach instead of stopping on growth-budget breach

A growth-budget breach no longer escalates to a maintainer handoff that
stops the takeover. The breach now makes the round a growth-audit round:
the agent audits the PR's approach on two axes — KISS (name a simpler
alternative or prove each piece load-bearing) and minimal change (every
hunk traces to the problem, an accepted finding, or a failing check) —
and records a machine-readable verdict that the verification gate
requires. sound re-arms the counting window at the current size and the
loop keeps solving; drift simplifies first, then continues; conflict is
the only growth path to a human, parked idempotently until a trusted
human responds.

The old divergence ladder (over budget for N rounds and not shrinking →
stop) terminated takeovers whose remaining work could still fit: the
growth it punished was protocol-mandated pinned tests (#9213 stalled at
round 5 with two small Criticals left). A size signal now triggers a
judgment, never a stop.

Design: docs/design/autofix-growth-audit.md

* fix(autofix): update the artifact-list pin for the growth-audit.json upload entry

* fix(autofix): surface conflict verdicts past the failure.md exits and strip verdict forgery channels (#9262)

* fix(autofix): harden the growth-audit verdict pipeline and park wake set (#9262)

* fix(autofix): close the verdict-pipeline forgeries and loop-generated wake entrances (#9262)

* fix(ci): drop the retired divergence rationale records (af-046/af-047) from qwen-autofix.md

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-21 04:54:07 +00:00
易良
a074d3b042
chore(ci): Disable install scripts in release CI and guard security-checks workflow (#9577)
* chore(ci): Disable install scripts in release CI and guard security-checks workflow

* fix(ci): complete release install hardening

* test(ci): pin release install step count

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

* fix(ci): scope release PAT to push step

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

* fix(ci): export GH_TOKEN so the release-branch push uses CI_BOT_PAT

* fix(ci): export GH_TOKEN so the credential helper sees it at push time

An inline GH_TOKEN prefix only covers the gh auth setup-git call itself;
the helper re-resolves the token when git push invokes it, so the push
would fall back to the job token with persist-credentials disabled.

* fix(test): anchor setup-git ordering check after the export line

A comment in the push step mentions gh auth setup-git before the export,
so indexOf found the comment first and the ordering assertion inverted.

* style(test): wrap long line to satisfy prettier

* fix(ci): address review findings on PAT handling and install comments

- Pin gh auth setup-git before the git push it authenticates in both
  release and finalize workflow tests, so moving credential setup after
  the push no longer passes.
- Correct the replay comment: npm run generate is not a lifecycle
  script and workspace lifecycle scripts stay disabled.
- Drop the overstated push-boundary claim and record why the push
  needs the bot PAT rather than the job token.

* test(ci): pin CI_BOT_PAT out of install steps and the publish job header

* style(test): apply prettier's exact re-wrap for the two flagged calls

* test(ci): pin CI_BOT_PAT out of the workflow-level headers too

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-21 03:48:03 +00:00
易良
0baaec2b32
chore(ci): Drop NPM_TOKEN in favor of npm Trusted Publishing (#9552)
* chore(ci): Drop NPM_TOKEN in favor of npm Trusted Publishing

* chore(ci): Pin npm 11 for Trusted Publishing in release jobs

* test(ci): Cover Trusted Publishing requirements
2026-08-20 08:31:41 +00:00
易良
b219e3a716
chore(ci): Add --provenance to npm publish and id-token permission (#9532)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Security Checks / Dependency CVE audit (push) Waiting to run
Security Checks / Secret scan (TruffleHog) (push) Waiting to run
* chore(ci): Add --provenance to npm publish and id-token permission

* test(scripts): expect --provenance in npm publish step assertion

PR #9532 adds --provenance to every npm publish in the release pipeline.
Update the workflow-pinning test to match the new command so the helper
test suite stays green.
2026-08-20 05:21:02 +00:00
Shaojin Wen
7942197666
fix(tests): apply integration worker limits to forks (#8689)
* fix(tests): apply integration worker limits to forks

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

* fix(tests): keep no-AK integration gate at two fork workers (#8689)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-ci-bot <25325202+qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
2026-08-08 00:53:33 +00:00
易良
c73b5ed887
ci: run Windows merge queue tests on ECS (#8386)
* ci: run Windows merge queue tests on ECS

* test(channels): skip POSIX mode assertion on Windows

* ci: expose Git Bash on Windows ECS runner

* ci: scope Windows ECS tuning to self-hosted and restore full test:ci

Review feedback on the Windows ECS routing: dropping test:scripts removed the only Windows execution of 9 Windows-only install-script tests, and the job-wide PowerShell default plus narrowed test command changed the kill-switch fallback away from the known-good hosted configuration.

Restore the full npm run test:ci on both paths (bash is available: pre-installed on hosted runners, exposed via the Git Bash PATH entry on ECS) and gate every ECS-specific adjustment on runner.environment: the PowerShell setup step (now also skip_ci-guarded), TEMP/TMP/LC_ALL env writes, and the Linux-style Node setup split that fails with an actionable error naming MAINTAINER_ECS_RUNNER_DISABLED. The windows-2022 fallback is byte-for-byte the pre-ECS job again.

* test: make Windows CI suites platform-aware

* ci: add stale-checkout guard to Windows ECS test job

* test(core): compare canonical directory identity

* ci: add fork guard and review follow-ups to Windows ECS job

* test(core): exercise real directory identity change

* test(core): wait for killed lease process exit

* test(scripts): avoid cmd echo trailing spaces

* test(scripts): use unambiguous cmd echo syntax

* test(cli): avoid sidecar I/O in truncation test

* test: fix Windows script-suite gaps and unify platform gating

- Fix missed trailing-space cmd stub in package-scripts.test.js so the
  'runs prepare steps in order' assertion passes on Windows.
- Add qwen-pr-review-workflow.test.js and pr-self-report-label.test.js to
  the win32 exclude list (both test Linux-only workflows and are not
  portable to Windows).
- Replace local itPosix/describeOnNonWindows consts with vitest's built-in
  it.skipIf/it.runIf/describe.skipIf, matching the codebase idiom.

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

* test(scripts): restore Windows workflow coverage

* test(scripts): re-exclude Windows-incompatible workflow tests on win32

Re-add pr-self-report-label.test.js and qwen-pr-review-workflow.test.js to
the win32 exclude list. Both fail on a Windows runner for reasons the code
still carries: qwen-pr-review-workflow.test.js calls execFileSync('mkdir'),
which has no executable to resolve there, and pr-self-report-label.test.js
joins PATH with ':', corrupting the ';'-separated Windows PATH so its gh
stub never resolves. Excluding them restores a green Windows gate; Linux CI
remains their authoritative coverage. Document the criterion inline.

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

* ci: extract checkout-head guard into composite action, pin Windows gate (#8386)

Address review round 2: move the stale-checkout guard shared by the four CI gates into .github/actions/verify-checkout-head so the copies cannot drift, pin the Windows gate kill-switch routing and guard wiring in the script tests, re-enable lint.test.js on Windows via separator normalization and a lazy linter setup in scripts/lint.js, unify the platform skips on it.skipIf(process.platform === 'win32'), and document the queued-run behavior of the ECS kill switch.

* ci: fail fast in Windows gate environment setup (#8386)

* ci: dedupe self-hosted runner steps into actions, pin gate mutations (#8386)

* fix(ci): checkout before repository-local actions in Windows gates (#8386)

* fix(ci): configure Windows runner before bash guard

* test(ci): pin remaining shared-action wiring in script tests (#8386)

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

* fix(ci): skip zip-dependent packaging tests when zip is missing (#8386)

* fix(ci): validate full Windows smoke path

* fix(ci): match Windows smoke shell to gate and drop dead runs-on guard (#8386)

* fix(ci): make SIGTERM escalation test Windows-aware and tighten pins (#8386)

The CDP acceptance test asserted a POSIX-only SIGKILL escalation, which
fails deterministically on Windows where kill('SIGTERM') terminates the
child directly — blocking the Windows merge-queue gate. Assert the
platform-appropriate signal instead.

Also address review suggestions: probe `unzip` alongside `zip`, pin the
integration_cli guard's missing step-level `if:`, stop getWorkflowStep
at unnamed steps, pin install-script.test.js out of the win32 excludes,
add the stale-checkout guard to windows-runner-smoke.yml, pin the
Node preflight warning branch and the guard reject path contiguously,
and extend the smoke shell-parity loop to the npm cache step.

* docs(ci): clarify Windows runner trust boundary

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
2026-08-05 12:14:42 +00:00
Shaojin Wen
da37110e60
perf(autofix): build the review CLI bundle once per scan and fan it out to legs (#8548)
* perf(autofix): build the review CLI bundle once per scan and fan it out to legs

Each review-address leg repeated the same trusted-base build: measured
3.5-5 minutes of npm ci + build + bundle per leg (~25 runner-minutes on
one 6-leg scan) before the agent could start. A build-cli job now
compiles the bundle once per scan, uploads the repo-root dist/ as an
artifact, and the legs download it; their checkout is pinned to the
compiled SHA so a mid-run base push can never pair a leg's bundle with
different sources. The legs keep npm ci (the agent and the verify gate
still need node_modules against the PR branch), and the issue phase is
untouched — it runs only when no review targets exist, so gating the
build on do_issue too would rebuild on every quiet scheduled tick.

* fix(autofix): validate fan-out bundle SHA and pin shared CLI recipe contracts (#8548)

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

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-08-05 05:16:49 +00:00
易良
c55e63c42a
fix(release): bump preview base past published stable (#7978)
* fix(release): bump preview base past published stable

When the nightly tag base (e.g. 0.21.0) is already published as stable,
getPreviewVersion() now bumps the patch (→ 0.21.1-preview.0) instead of
deriving a preview for the released version. This prevents the scheduled
Tuesday preview release from hitting npm E403 on channel packages that
were already published at that version.

Fixes #7969

* fix(release): address preview version review feedback

* fix(release): reject divergent preview baseline

* fix(release): skip already published packages

* test(release): cover publish skip guards

* fix(release): bump preview past newer stable

* fix(release): address review feedback on preview version guard

- Expand doesVersionExist to check all 10 published packages instead of
  only @qwen-code/qwen-code, so the auto-increment loop detects versions
  taken on sibling channel packages.
- Use the rollback-aware getAndVerifyTags lookup for the latest stable
  instead of the raw dist-tag, preventing a retrograde preview base when
  the dist-tag has been rolled back.
- Emit :⚠️: in the channel publish loop when every package was
  already published, making a fully-skipped release visible.
- Move preview stable-guard tests to Advanced Scenarios, add npmTag and
  previousReleaseTag assertions, add a non-bump boundary case, and assert
  the subshell wrapper and all-skipped warning in the workflow test.

* test(release): cover channel package version conflicts

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-29 23:21:00 +00:00
qqqys
b1ce0c2087
refactor(autofix): extract review verification runner (#7644)
* refactor(autofix): extract review verification runner

* test(ci): follow extracted autofix verifier

* docs(autofix): document the review verification runner env contract (#7644)
2026-07-24 16:46:24 +00:00
Shaojin Wen
eca654f365
fix(autofix): resolve owning package for nested paths; report verify-failed handoffs as not pushed (#7330)
* fix(autofix): resolve owning package for nested paths; report verify-failed handoffs as not pushed

The verify gate mapped each changed file to a flat `packages/<dir>` and
read `<dir>/package.json`, which ENOENT-crashed on nested packages such
as packages/channels/base — the container packages/channels has no
package.json. Walk each changed file up to its nearest package.json in
both the issue-fix and review-address verify steps, and skip any
candidate that still has none.

When such a verify failure follows an agent commit, the review-address
handoff rendered the agent's optimistic address-summary.md (which can
cite a commit SHA) under a neutral "what I found" heading, so a
maintainer chased a commit that was discarded with the runner workspace.
An EXIT trap now records any post-commit non-zero exit as outcome=failed,
and the handoff states plainly that the change did NOT pass the gate and
was NOT pushed.

Tests: walk-up detection over a nested package tree, the outcome=failed
trap, and the not-pushed handoff wording — each mutation-verified.

* refactor(autofix): extract owning-package resolver to a shared staged script

Addresses review on #7330.

Extract the changed-file → owning-package walk into
.github/scripts/resolve-owning-packages.sh, staged to RUNNER_TEMP from the
trusted base alongside check-settings-schema.sh and invoked from both verify
gates, so the two gates cannot drift into resolving packages differently (the
8-line walk was otherwise duplicated verbatim in each). Updates the
package-scripts test that pinned the old inline grep.

Narrow the verify-failed handoff lead-in to "This change was NOT pushed": four
paths set outcome=failed BEFORE the deterministic gate runs (agent abort via
failure.md, dirty tree, unchanged branch, missing address-summary.md), so the
previous "did NOT pass the verification gate" claim was factually wrong for
them. The specific reason stays in the headline and the quoted summary.

* style(autofix): brace variable references in resolve-owning-packages.sh

The repo's shellcheck gate runs --enable=all --severity=style, under which
bare $f/$d references trip SC2250 (prefer ${var}). Brace them to match the
convention already used in check-settings-schema.sh, and update the script
content assertions accordingly. Verified with shellcheck 0.11.0 using the
exact CI flags: clean.

* fix(autofix): resolve owning workspace via npm query; key unpushed-handoff on commit existence

Addresses the deeper review on #7330.

Blocking issue: the "nearest package.json" resolver mapped a change under a
workspace's fixture/example package (e.g.
packages/cli/src/commands/extensions/examples/starter) to that fixture, whose
test script is not Vitest — silently SKIPPING packages/cli's own tests, a
coverage regression invisible in the log. Resolve against the authoritative
`npm query .workspace` set instead and take each file's longest-prefix
workspace: nested workspaces (packages/channels/base) match exactly, fixtures
and non-workspace paths (packages/sdk-python, packages/README.md, the excluded
packages/desktop) drop. Also harden the resolver against a final line with no
trailing newline and against an unmatched last line, which under
`set -o pipefail` would otherwise abort the script.

Handoff wording: keying "was NOT pushed / commit discarded" on outcome=failed
was wrong for the abort paths (failure.md, dirty tree, unchanged branch,
missing address-summary.md), which set outcome=failed before ever making a
commit. Record committed=true right after checkout — before any gate can fail
— and key the wording on that; the abort/no-op paths keep the neutral framing.
This removes the EXIT trap entirely (its only observable effect was that
wording), so it no longer mislabels pre-commit failures either.

* fix(autofix): expand workspaces on-disk so branch-added packages are tested; harden resolver

Addresses the re-review on #7330.

The resolver sourced its workspace set from `npm query .workspace`, which reads
node_modules — installed from the BASE checkout. A workspace the PR branch ADDS
(a new channel adapter, a new sdk — the issue-fix job's whole purpose) was
invisible, so its tests were silently skipped, and for a nested new package the
ENOENT crash this PR fixes turned into a silent skip. Expand the set from the
on-disk root package.json `workspaces` globs instead (shallow `dir/*` + literals,
honouring `!` negations, keeping dirs with a package.json): it reflects the
branch, matches what `npm run --workspace` accepts downstream, and needs no
install. Verified to reproduce `npm query`'s set exactly on the current tree.

Also from the review:
- Fail the gate loudly on an empty/unreadable workspace set instead of the
  silent "no package changes" skip, and drop the now-unneeded `|| true` at both
  resolver call sites (the resolver already exits 0 on legitimate no-match).
- Record committed=true at the TOP of the step (ref-only diff), covering an
  agent that commits then aborts, and count only `git diff --quiet` exit 1 as a
  commit (128 is a git error, not a discarded commit).
- Correct the two call-site comments that still described the superseded
  nearest-package.json approach.

Also hardens the resolver against a final changed-path with no trailing newline
and an unmatched last line under `set -o pipefail`.

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-20 14:39:56 +00:00
Shaojin Wen
582fb49603
feat(web-shell): git status chip, visual working-tree diff, and sidebar git status (#7054)
* feat(web-shell): git status chip, visual working-tree diff, and sidebar git status

Bring working-tree Git awareness to the Web Shell (browser daemon session UI):

- Toolbar branch chip becomes a live status indicator: dirty (staged/unstaged/
  untracked), ahead/behind upstream, stash count, detached HEAD, in-progress
  operation (merge/rebase/cherry-pick/revert/bisect), and conflict count, each
  with a non-color cue.
- Read-only "Changes" dialog: working-tree-vs-HEAD file list with per-file,
  line-level, per-side syntax-highlighted diffs; opens via /diff or a dirty
  chip; untracked files expand as fully-added and deleted files still diff.
- Per-workspace git status in the sidebar: a compact icon-only chip per trusted
  workspace (status dot + hover tooltip); click opens that workspace's dialog.

All git access goes through the daemon REST API with per-workspace trust
gating; new SDK status fields are optional and additive (v2).

* fix(web-shell): themed tooltips and git-chip review follow-ups

Tooltips now render on the themed popover surface (bg-popover /
text-popover-foreground / border + fill-popover arrow) instead of the
inverted bg-foreground default, so they read dark-on-dark rather than a
bright box on the dark theme. Fixing the shared primitive corrects the
git branch tooltip in the composer toolbar and sidebar, plus every other
tooltip, at once.

Also addressing review feedback on the git integration:
- Replace the hand-drawn detached/conflict/stash SVG icons with
  lucide-react (CircleDot / TriangleAlert / Layers) per the web-shell
  icon convention.
- Gate the tooltip "Working tree clean" message on an enriched status
  (computedAt) so a branch-only status no longer asserts clean.
- Include the file path in the diff dialog row aria-label so screen
  readers can distinguish files.
- Reset the toolbar git chip on workspace switch so it never shows the
  previous repo's branch/counts while the new fetch resolves.
- Log a sidebar git poll failure only on the success->failure transition
  to avoid spamming a long-lived tab.
- Correct the SDK doc for DaemonWorkspaceGitDiffFile.added/removed
  (0, not undefined, for binary files).

* fix(web-shell): address git-integration review suggestions

Follow-ups from the /review pass on the git integration:

- GitBranchIndicator: include the short SHA in the detached-HEAD tooltip
  title, and add the "Working tree clean" status to the aria-label (gated
  on an enriched status, matching the tooltip) so the two never drift.
- WorkspaceSection: keep the last known git status on a transient poll
  failure instead of blanking the chip for a whole interval.
- App: surface a toast for `/diff` when no workspace is available instead
  of silently consuming the composer input.
- Tests: cover the diff dialog's list-load and per-file load error paths,
  and detectGitOperation's revert/bisect branches.
- Design doc: align the getGitWorkingTreeStatus spec text with the
  decision (transient states return status with `operation`; null is
  reserved for non-repo / git failure).

* fix(web-shell): focus-visible ring for git chip button; align doc poll interval

- Add a :focus-visible outline to .gitBranchChipButton so keyboard users
  get a visible focus indicator (the chip resets UA button chrome).
- Design doc: align the active-workspace poll-interval references at 30s
  to match the implementation.

* fix(web-shell): surface capped diffs, catch row-build failures, cover degradation paths

Address the remaining review findings on the git integration:

- Truncation is no longer silent: fetchGitDiffHunksForFile now returns
  { hunks, truncated } — the parser records files that actually lost
  lines to MAX_LINES_PER_FILE (tracked path), and the untracked
  synthesis reports its byte/line caps. The route forwards an additive
  `truncated` flag on the hunks response (absent when not truncated, so
  older clients and daemons are unaffected), and the Changes dialog
  renders a "Diff truncated" note under the visible window.
- DiffHunks catches an unexpected buildRows rejection (e.g. malformed
  hunk lines) and shows the per-file error instead of leaving an
  unhandled rejection and a silently empty diff area.
- New tests: untracked and tracked truncation at the core caps, the
  route's truncated passthrough (and its absence when clean), the
  branch-only degradation when the working-tree summary throws, the
  malformed-hunks error path, and the Shiki success path (a fake
  tokenizer proving add rows pull new-side tokens and del rows pull
  old-side tokens, not the plain-text fallback).

* fix(web-shell): drop dialog backdrop-blur that froze the page on open

The dialog and alert-dialog overlays applied `backdrop-blur-xs`, which
forces the browser to rasterize and blur the entire content behind the
overlay when a dialog opens. With a long transcript behind it, that
main-thread paint+blur froze the whole page — e.g. clicking the git
branch chip to open the Changes dialog. Keep the bg-black/10 scrim for
separation and drop the blur.

* fix(core): guard synthesizeUntrackedHunk against non-regular files

synthesizeUntrackedHunk opened an untracked path before checking its
type, so an untracked FIFO (listed by `ls-files --others`) would block
on open() forever waiting on a writer — hanging the daemon's event loop
and leaving the Web Shell Changes dialog stuck on a permanent loading
state. lstat-gate on regular files before opening, matching the existing
guard in countUntrackedLines. Adds a FIFO regression test.

* fix(web-shell,core): rename expansion, no-newline marker, chip measurement

Round-5 review Criticals:

- core: key renamed diff entries by the real (post-rename) path and carry
  the old path for display, so renamed rows can be expanded — the synthetic
  `old => new` key was sent to git as a nonexistent literal path. The diff
  dialog renders the rename as `old → new`.
- core: preserve Git's `\ No newline at end of file` marker through the hunk
  parser so a trailing-newline-only edit isn't shown as identical
  removed/added lines (the viewer already renders it as a meta row).
- web-shell: the toolbar's hidden git-chip measurement replica now renders
  the full chip content via the extracted GitBranchChipContent, so the
  expanded width includes the status indicators and the compact/expanded
  toggle no longer oscillates near the responsive threshold.

* fix(build): generate git-commit info even when prepare build is skipped

The review tooling runs `npm ci` with QWEN_SKIP_PREPARE=1 (to skip the
heavy prepare build) and then builds only the changed workspaces. Because
`prepare` exited before generating the gitignored git-commit.ts, a
per-workspace build of packages/cli failed at the unchanged systemInfo.ts
on the missing `../generated/git-commit.js` module. Generate the git-commit
info in the skip path too — it is cheap and never fails hard — so a later
per-workspace build or typecheck finds the module. The non-skip path still
generates it via `npm run build`.

* fix(web-shell,cli): address round-6 review suggestions

- cli: carry the pre-rename path (oldPath) through DiffRenderRow and show
  renamed files as `old → new` in both the Ink and plain-text renderers.
  The rename-keying fix updated the daemon and web-shell dialog but not the
  CLI `/diff` renderer, which silently dropped the old path.
- web-shell: key DiffFileRow by workspace + path so switching workspace
  remounts the row instead of reusing another workspace's hunks/open state
  for a path both workspaces share.
- web-shell: show a loading placeholder in DiffHunks while rows are (re)built
  (e.g. after a theme switch) instead of an empty, jumpily-resized box.
- web-shell: cover the /diff local intercept in App.test.tsx (opens the
  Changes dialog and is not forwarded to the agent).

* fix(web-shell,cli,core): address round-7 review suggestions

- cli: sanitize the rendered filename (and pre-rename oldPath) in the Ink
  DiffStatsDisplay via sanitizeFilenameForDisplay, matching the plain-text
  renderer so a crafted path can't inject into the interactive view.
- cli: apply the read headers before awaiting the per-file diff fetch (as
  handleDiffList does) so error responses also carry no-store/nosniff.
- cli + web-shell: strip Unicode bidi embedding/isolate controls
  (U+202A-202E, U+2066-2069) in the filename/control-char sanitizers so a
  crafted filename can't visually spoof its extension.
- core: guard countStashEntries with an lstat type check before readFile, so
  a symlink-to-FIFO at logs/refs/stash can't block the event loop (the same
  hazard already guarded in the untracked-file readers).
- core: cover fetchGitDiffHunksForFile's transient-state guard with a test
  (the sibling helpers already had one).

* fix(web-shell,cli,core): address round-8 review suggestions

- core: pass --no-optional-locks to the ls-files call in
  fetchGitDiffHunksForFile, matching the other runGit calls so it doesn't
  contend for an optional index-refresh lock alongside concurrent git
  add/commit.
- cli: add a route test asserting a rename's oldPath survives serialization
  end-to-end (keyed by the new path, old path carried alongside).
- web-shell: add a GitDiffDialog test for the hiddenCount>0 "N more files
  not shown" note (every payload previously used hiddenCount: 0).
- web-shell: drop the nonexistent primaryLabel prop from the WorkspaceSection
  test (it is not a WorkspaceSectionProps member).
- docs: correct the plan doc — large-diff virtual scrolling was explicitly
  descoped (core caps + per-file lazy loading), not implemented in Phase 2.

* fix(cli,web-shell): address round-9 review findings

- cli: propagate the pre-rename oldPath through DiffDialog's
  perFileToUnified and render renamed files as `old → new` in the
  interactive diff viewer (the rename-keying fix had updated the daemon,
  the web-shell dialog, and the /diff stats, but not this viewer).
- cli: cover DiffStatsDisplay's rename (`old → new`) rendering and the
  sanitizeFilenameForDisplay path for hostile filenames carrying control
  characters.
- web-shell: guard the GitBranchIndicator test afterEach against
  double-unmounting an already-unmounted root (the localization tests
  assert on getTranslator without calling render()).

* fix(core,cli,web-shell): rename-aware single-file diff (old→new)

fetchGitDiffHunksForFile pathspec-limited the diff to the new path, which
defeats git's rename detection — a renamed file was reported as fully
added (every line +) instead of its actual edit. Thread an optional
pre-rename path through the single-file endpoint (core → route → SDK →
dialog) and diff old→new with -M when it is present, so expanding a
renamed file shows its real content change.

* fix(cli): address round-10 review suggestions

- DiffDialog: split the path-width budget between old and new paths for a
  rename (reserving the " → " separator) so the combined width stays within
  maxPathChars instead of overflowing the row layout.
- textUtils: extend MULTILINE_CONTROL_CHARS_REGEX with the Unicode bidi
  ranges (matching FILENAME_CONTROL_CHARS_REGEX) and add a test that
  sanitizeFilenameForDisplay strips bidi embedding/isolate controls.
- workspace-git-diff route: add a test that ?oldPath= is parsed and
  forwarded to fetchGitDiffHunksForFile.

* test(sdk),docs: cover diff client methods; align design doc

- sdk: add DaemonClient unit tests for workspaceGitDiff() and
  workspaceGitDiffFile(path, oldPath?) — URL construction (incl. urlEncode
  on path/oldPath, with and without oldPath, plus the workspace-qualified
  route) and response deserialization, mirroring the existing workspaceGit()
  test.
- docs: add the oldPath? param to the workspaceGitDiffFile API spec; record
  that the diff client methods now have unit tests (correcting the claim
  that workspaceGit() had none); attribute the bundle-limit bump to
  packages/sdk-typescript/scripts/build.js; clarify ahead/behind are relative
  to upstream (0, and ↑N/↓N not shown, without one).

* fix(web-shell,core): address round-11 review suggestions

- GitBranchIndicator: count conflicted entries as dirty — a merge where every
  changed file is conflicted (staged=unstaged=untracked=0) is still
  uncommitted, so the expanded chip's dirty dot / data-dirty now reflect it.
- core: split the status branch line at the last "..." (the branch/upstream
  separator) so a dotted branch name isn't truncated at the first "...".
- GitDiffDialog: guard DiffFileRow's in-flight fetch against unmount via a
  cancelled ref, matching DiffHunks / GitDiffDialog.
- tests: forward oldPath when expanding a renamed file in the web-shell
  dialog; bidi-strip coverage for the web-shell sanitizeControlChars;
  untrusted-guard coverage on the single-file diff route; conflicted-only
  dirty; branch-line "..." split.

* fix(web-shell,cli): address round-12 review suggestions

- DiffDialog: only render the rename "old → new" when there's room for both
  sides (≥19 cols, so each gets ≥8); otherwise fall back to the new path
  alone, so a narrow terminal no longer overflows the row (the Math.max(8,…)
  floor could exceed maxPathChars).
- GitBranchIndicator test: guard afterEach container.remove() for non-render
  tests run in isolation, and make the compact-mode ↑-suppression assertion
  non-vacuous by giving the fixture an ahead count.
- App: compute the active workspace once (useMemo) and share it between the
  git-status effect and the Changes-dialog entry point, so the chip and the
  dialog can't drift onto different repos.

* fix(core,docs): address round-13 review suggestions

- core: add a rebase-apply detection test (git am / an interrupted
  `rebase --apply` creates rebase-apply, which detectGitOperation also maps
  to 'rebase'); previously only rebase-merge was exercised.
- docs: correct section 5 to describe the actual diff-dialog mechanism
  (diffWorkspaceCwd state, not the stale activePanel design).

* test(core): cover stray no-newline marker before any hunk header

parseGitDiff's pre-hunk guard already skips a "\ No newline at end of
file" marker that appears before any @@ header, so a malformed/truncated
diff can't throw on a null currentHunk and lose subsequent files' hunks;
add a regression test pinning that behavior.

* fix(web-shell): unstick per-file diff loading and skip non-path git poll

- DiffFileRow: reset the cancelled-fetch flag on mount so StrictMode's
  mount/unmount/mount replay no longer leaves it latched at true, which
  dropped the fetched hunks and froze the row on "Loading changes…" despite
  a 200 response.
- WorkspaceSection: skip the git status poll when the workspace cwd is not an
  absolute path. A synthetic fallback workspace carries a display name there,
  which the cwd-qualified route rejects with a 400.

* fix(web-shell,cli): address review suggestions on the git diff surface

- GitDiffDialog: highlight each diff side independently so a small side
  keeps syntax highlighting even when the other side exceeds the size cap
  (the old guard dropped both as soon as either was too large).
- ChatEditor: complete the .gitBranchChipButton reset (font/color/padding/
  margin) so the clickable dirty-tree chip matches the read-only output chip
  instead of picking up UA button styling.
- DiffDialog: cover the interactive rename display (old to new on a wide
  terminal), mirroring the rename tests DiffStatsDisplay and GitDiffDialog
  already have.

* test(web-shell,cli): cover git chip clean/reload/traversal paths, fix doc

- GitDiffDialog: add the missing expect(header).not.toBeNull() guard to the
  three expand-file tests that lacked it, matching the others in the block.
- GitBranchIndicator: cover the known-clean aria-label branch (computedAt set
  and every change counter zero).
- WorkspaceSection: verify a reloadToken change re-fetches git status instead
  of waiting for the next 60s poll.
- workspace-git-diff route: verify a traversal oldPath is forwarded to core
  and surfaced as available:false rather than escaping the workspace.
- Design doc: /diff is handled via setDiffWorkspaceCwd, not setActivePanel.

* fix(core): allow literal `..foo` paths in diff normalization

- toRepoRelativePath: reject only a real climb-out (`..` or `../…`), not a
  literal `..foo` filename at the repo root, which the bare startsWith('..')
  over-rejected, leaving the diff viewer unable to render such a file.
- parseGitDiff: cover the truncatedPaths output set directly (it was only
  exercised indirectly through fetchGitDiffHunksForFile).

---------

Co-authored-by: wenshao <wenshao@example.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
2026-07-18 10:06:07 +00:00
callmeYe
3d4601489e
revert: remove local PR verification gate (#7031)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
Reverts QwenLM/qwen-code#6873 and QwenLM/qwen-code#7025.
2026-07-16 11:24:38 +00:00
callmeYe
441006b0e1
feat(scripts): add local PR verification gate (#6873)
* feat(scripts): add settings schema check mode

* feat(scripts): add local PR verification runner

* fix(scripts): harden local PR verification

* docs: document local PR verification gate

* fix(scripts): isolate local verification tools

* fix(scripts): scope PR formatting checks

* fix(scripts): skip symlinked PR paths

* fix(scripts): preserve verification gate integrity

* fix(scripts): canonicalize verification temp paths

* fix(scripts): stabilize local PR verification

* fix(scripts): clear built-in test credentials

* fix(scripts): enforce isolated test environment

* fix(scripts): serialize local verification tests

* fix(scripts): address PR verification review

* fix(scripts): preserve review git wrapper environment

* refactor(scripts): avoid step helper shadowing

* fix(scripts): distinguish forwarded child signals

* fix(scripts): preserve relayed signal exit codes
2026-07-15 00:58:17 +00:00
易良
6d966d2917
test(core): stabilize file history eviction test (#6637)
* test(core): stabilize file history eviction test

* ci: clean package build artifacts before fast-path check

* ci: preserve web build outputs during fast-path check
2026-07-10 06:39:52 +00:00
易良
e3906392b5
perf(ci): optimize autofix pipeline — fast-track, skip duplicate build, scoped tests (#6315)
* perf(ci): optimize autofix pipeline — fast-track, skip duplicate build, scoped tests (#6196)

Three optimizations to reduce autofix wall-clock from ~48 min to ~28-35 min:

1. Fast-track decision: skip LLM assessment for trusted triggers
   (workflow_dispatch with explicit issue, issues:labeled event).
2. Skip duplicate build: set QWEN_SKIP_PREPARE=1 so npm ci does not
   re-run the prepare hook that builds+bundles before the explicit
   build step.
3. Scoped verification tests: run only changed-file tests via
   --changed instead of full per-package suites. Full regression
   is covered by regular CI on the PR.

Also hardens live test gating (require QWEN_CODE_RUN_LIVE_TESTS=1)
and increases crawler test timeout to 30s.

* refactor(ci): simplify verification gate package discovery

Replace 30-line inline Node script with the original one-liner
`grep -oE '^packages/[^/]+'`. The Node script checked for
package.json existence and test scripts, but `--if-present` already
handles missing test scripts and all workspace dirs have package.json.

Addresses design-review item #7.

* fix(ci): tighten autofix changed-test gate
2026-07-05 02:32:04 +00:00
易良
b16baf1ffc
ci(release): optimize validation steps (#6133)
* ci(release): optimize validation steps

* fix(ci): address release validation review comments

* fix(ci): document prepare skip contract

* test(ci): cover prepare failure exit

* fix(ci): prefix prepare error logs

* fix(ci): keep release quality build artifacts

* fix(ci): address release validation comments

* fix(ci): preserve release publish hooks

* test(ci): cover prepare failure paths

* fix(ci): disable release coverage safely
2026-07-02 23:53:08 +00:00
jinye
ea536d361f
fix(cli): Handle ACP read_file for managed local paths (#6021)
* fix(cli): handle ACP read_file local roots

Allow ACP read_file calls to fall back to local reads for explicitly permitted local roots when the serve workspace boundary rejects them, and preserve useful messages for plain object read errors.

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

* codex: address PR review feedback (#6021)

Add the missing getUserAutoMemoryRoot export to the acpAgent test core mock so the updated acpAgent import resolves under Vitest.

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* fix(acp): Narrow local read fallback temp roots

Remove the broad OS temp directory from default read_file allow roots and ACP local read fallback roots. Keep qwen-managed temp roots readable and reuse the shared isSubpath helper after realpath resolution for ACP fallback containment.

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

* codex: fix CI failure on PR #6021

Add the serve fast-path bundle check script and root npm script that the current CI workflow invokes. This mirrors the already-merged mainline check without pulling unrelated workflow or test config changes into this PR.

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

* fix(cli): address ACP read error review feedback

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

* fix(cli): harden ACP error normalization

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: fix Windows CI path expectation (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

* codex: address PR review feedback (#6021)

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

---------

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-06-30 11:15:49 +00:00