mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
main
18
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
f8256e8222 |
fix(release): derive the next patch when the preview baseline lags latest (#16470)
* fix(release): derive the next patch when the preview baseline lags latest (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): derive the stable bump from the rollback-aware latest (#16466) Co-authored-by: Qwen-Coder <[email protected]> * refactor(release): share the published-latest bump helper (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse cross-line stable versions from older refs (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse stable releases from refs behind latest (#16466) Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse cross-line and stale stable derivations (#16466) Co-authored-by: Qwen-Coder <[email protected]> * test(release): pin the cross-line guard's strict boundary (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): refuse a derived base above the checked-out ref (#16466) Co-authored-by: Qwen-Coder <[email protected]> * fix(release): keep same-line next-patch promotions allowed (#16466) Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: qwen-code-dev-bot <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
61697df9b0 |
feat(external-context): Publish the Mem0 Extension package (#10653)
* feat(external-context): publish Mem0 extension package Co-authored-by: Qwen-Coder <[email protected]> * test(release): Guard Mem0 manifest write-back Co-authored-by: Qwen-Coder <[email protected]> * fix(release): Normalize Mem0 manifest updates Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4736927000 |
fix(ci): verify previous release tag exists in git before anchoring release notes (#10167)
* fix(ci): verify previous release tag exists in git before anchoring release notes * test(ci): cover the no-stable-git-tags branch of previous tag resolution * fix(ci): filter unparseable tags and pin semver sort order - Add semver.valid() guard to listExistingStableTags so tags with leading zeros (e.g. v01.2.3) are dropped instead of crashing the sort - Add test for unparseable-tag filtering (v01.2.3 in tag list) - Add test for semver vs lexicographic sort (v0.9.0 vs v0.10.0) |
||
|
|
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) <[email protected]>
* 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
|
||
|
|
f7f78fab4a |
fix(ci): force-push release branch so retries replace failed attempts (#9076) (#9082)
* fix(ci): force-push release branch so retries replace failed attempts (#9076) Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): re-validate the release version before force-pushing (#9076) prepare's doesVersionExist check runs minutes to hours before publish pushes (validation jobs and the production-release approval gate sit in between), and --force removed the non-fast-forward rejection that used to serialize the push itself. Concurrent same-version runs could therefore diverge the npm artifacts, the git tag, and main. Serialize publish per release tag and re-validate the unshipped invariant — every published package, the tag, and the release — immediately before the push; pin all three invariants in the workflow tests. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): unify the push-time release guard and key concurrency by dry-run (#9076) * fix(ci): fail closed on push-time release probes and test the CLI seam (#9076) * fix(ci): clarify push-time release refusals and keep benign ones out of autofix (#9076) The push-time guard refused retries after a partial npm publish without saying where the version had shipped or how to recover, and every refusal failed the publish job into notify_failure, filing a "Release Failed" issue and dispatching the autofix agent against releases that did not fail. - Scan all published packages in strict mode and name every shipped location in the refusal (npm packages, origin tag, GitHub release) with partial-publish recovery guidance; a decisive hit ends the check so a flaky later probe cannot mask the refusal with a probe error. - Give the guard distinct exit codes: 3 = already shipped (decisive, benign), 2 = probe or usage failure. Exit 1 is reserved for uncaught node errors so a crash can never masquerade as the benign marker. The push step marks exit-3 refusals via the version_refusal job output, and notify_failure skips its issue + autofix dispatch for exactly that failure while genuine failures still notify. - Cover runCli's default dispatch (prepare's path), the exit-code contract, and the process.exit wiring end to end. * fix(ci): fail closed when the release ref predates the push-time guard (#9076) * fix(ci): keep refusals decisive after shipped hits and skip the POSIX-only test on win32 (#9076) * fix(ci): write push-time guard error annotations to stdout (#9076) Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
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 ::warning:: 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 <[email protected]> |
||
|
|
955ad27fc7 |
fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476) (#6481)
* fix(scripts): handle missing NPM dist-tags gracefully in release versioning (#6476) getAndVerifyTags now returns null instead of throwing when no baseline version exists on NPM. getPreviewVersion and getStableVersion fall back to the package.json base version, matching the pattern already used by getNightlyVersion. This prevents the release workflow from failing when no nightly or preview dist-tag has been published yet. * fix(scripts): harden release versioning against transient NPM errors and missing dist-tags (#6476) - Distinguish 404 from transient errors in getVersionFromNPM so NPM outages halt the release instead of silently falling back - Consult getAllVersionsFromNPM when dist-tag is missing to derive baseline from published versions rather than returning empty - Add console.error logging when getAndVerifyTags returns null - Validate package.json fallback version in getStableVersion and getPreviewVersion - Add tests for promote-nightly/patch throw paths, true greenfield scenario, versions-list derivation, and transient error propagation * fix(scripts): propagate transient NPM errors in getAllVersionsFromNPM (#6476) getAllVersionsFromNPM silently swallowed all errors including transient network failures (ETIMEDOUT, ECONNRESET), which became load-bearing now that the missing-dist-tag fallback depends on it. Match the same 404-only pattern already used by getVersionFromNPM. Also fix a DRY violation in getPreviewVersion, correct misleading test comments, and add test coverage for the versions-list error path and latest filter branch. * fix(scripts): tighten 404 detection and tolerate transient versions-list failures (#6476) - Remove redundant '404' substring check; E404 is the canonical npm error code and bare '404' could false-match unrelated errors (port 4043, E4040) - Catch transient versions-list errors in detectRollbackAndGetBaseline when distTagVersion is already resolved, avoiding hard-blocking a release when rollback detection is merely a safety net - Update and add tests for the new fallback behavior and deprecated-versions path * fix(scripts): guard release version fallback edge cases * test(scripts): cover greenfield versions list E404 --------- Co-authored-by: Qwen Code Autofix <[email protected]> Co-authored-by: qwen-code[bot] <qwen-code[bot]@users.noreply.github.com> Co-authored-by: Qwen Code Autofix <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
2c93fd670c |
refactor: extract shared release helper utilities (#3834)
Move four duplicated utility functions (getArgs, readJson,
validateVersion, isExpectedMissingGitHubRelease) from the three
get-release-version.js scripts into a shared module at
scripts/lib/release-helpers.js so that changes only need to happen
in one place.
Also fixes a pre-existing bug in getArgs where argument values
containing '=' were silently truncated (e.g. --msg=a=b produced
{msg:'a'} instead of {msg:'a=b'}).
Closes #3795
🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code)
Co-authored-by: jinye.djy <[email protected]>
|
||
|
|
03f66bada5 |
feat(sdk-python): add PyPI release workflow (#3685)
* feat(sdk-python): add pypi release workflow Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): build cli before smoke test Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): tighten release conflict handling Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): harden python release workflow Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): tighten stable release guards Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): harden prerelease publish flow Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): reuse release branches on rerun Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): resume incomplete releases Co-authored-by: Qwen-Coder <[email protected]> * fix(release): tighten missing-release checks Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): resume stable release reruns Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): tighten release recovery guards Co-authored-by: Qwen-Coder <[email protected]> * test(sdk-python): cover release version edge cases Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): address release workflow review feedback Co-authored-by: Qwen-Coder <[email protected]> * refactor(sdk-python): address review feedback on release version script - Remove unreachable `if (type === 'stable')` branch in bumpVersion(); the stable path was dead code since getVersion() throws for all stable conflicts before calling bumpVersion(). Move nightly conflict throw to the call site for symmetry. - Rename getNextPatchBaseVersion → getNextBaseVersion to reflect that the function can return a prerelease base without incrementing patch. - Add test for preview+nightly coexistence where nightly base is higher. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(sdk-python): address remaining review feedback on release workflow - Fix failure-issue gate to read github.event.inputs.dry_run directly instead of steps.vars.outputs.is_dry_run (which is empty when early steps fail). Add --repo flag for gh issue create when checkout failed. - Add diagnostic state table to failure-issue body (RELEASE_TAG, PACKAGE_VERSION, PUBLISH_CHANNEL, RESUME_EXISTING_RELEASE, etc.) - Fix release-notes error swallow: only silence release not found / Not Found / HTTP 404, emit ::warning:: for other gh release view errors. - Improve validateVersion error messages to use human-readable format keys (X.Y.Z, X.Y.Z-preview.N) matching TS sibling convention. - Filter fully-yanked versions in getAllVersionsFromPyPI. - Add console.error log when stable is derived from nightly. - Add bash regex guard for inputs.version to prevent shell injection. - Use per-release-type concurrency groups (nightly/preview/stable). - Add jq null-guard checks for all 6 field extractions. - Remove misleading --follow-tags from git push (lightweight tags). 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(sdk-python): rename misleading test description The test asserts that preview/nightly releases return empty previousReleaseTag, but the name said "same-channel previous release tags" which implied non-empty values. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(sdk-python): address unresolved review comments on release workflow - Remove -z check in extract_field() that blocked preview/nightly releases (previousReleaseTag is legitimately empty for non-stable releases) - Use static environment.url since step outputs aren't available at job startup - Use skip-existing for resumed PyPI publish to fill in missing artifacts - Add AbortSignal.timeout(30s) to PyPI fetch to prevent indefinite hangs - Add downgrade guard for stable_version_override - Use GHA ::warning:: annotation instead of console.error for visibility - Separate yanked/non-yanked version lists so conflict detection includes yanked versions (PyPI still reserves those slots) - Filter current release from previousReleaseTag to avoid self-reference on resume - Add tests for yanked conflict detection, downgrade guard, and resume previousReleaseTag Co-authored-by: Qwen-Coder <[email protected]> * fix(sdk-python): address final review round on release version script - Fix getNextBaseVersion() first-release skip: use pyproject.toml version directly when PyPI has no stable versions instead of unconditionally incrementing - Fix getNextBaseVersion() off-by-one: change > to >= so equal prerelease base continues the existing line instead of incrementing patch - Add ::warning:: annotation when preview auto-bumps due to orphan git tags (tag exists without PyPI version or GitHub release) - Add set -euo pipefail to 5 workflow steps missing it: release_branch, persist_source, Create GitHub release, Delete prerelease branch, Create issue on failure - Fix 2 existing tests affected by first-release change, add 4 new tests 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) * fix(sdk-python): use stderr for GHA warning annotations to avoid corrupting JSON stdout console.log writes to stdout, which gets captured by VERSION_JSON=$(node ...) in the workflow and corrupts the JSON output for jq. Switch to console.error so ::warning:: annotations go to stderr (GHA recognizes workflow commands on both streams). Also add set -euo pipefail to the "Get the version" step for consistency with other workflow steps. 🤖 Generated with [Qwen Code](https://github.com/QwenLM/qwen-code) --------- Co-authored-by: jinye.djy <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
be633a80cc | 📦 Release qwen-code CLI as a Standalone Bundled Package (#866) | ||
|
|
eb95c131be | Sync upstream Gemini-CLI v0.8.2 (#838) | ||
|
|
14ea33063f | Merge tag 'v0.3.0' into chore/sync-gemini-cli-v0.3.0 | ||
|
|
ee4feea006 |
chore: consistently import node modules with prefix (#3013)
Co-authored-by: N. Taylor Mullen <[email protected]> |
||
|
|
0e24805806 |
feat(release): update release process for nightly and preview builds (#6643)
Co-authored-by: Bryan Morgan <[email protected]> |
||
|
|
999f3af098 | fix release workflow (#172) | ||
|
|
a9d6965bef | pre-release commit | ||
|
|
a7256f630c | Relase: Clean up and condensing (#3321) |