mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-10 17:27:10 +00:00
3867 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
732f4d8a29
|
feat(voice): support trusted private ASR base URLs (#8350)
* feat(voice): support trusted private ASR base URLs * fix(voice): address private endpoint review findings * test(voice): cover private endpoint edge cases * test(voice): pin remaining endpoint edge cases * fix(voice): address private endpoint review feedback * fix(voice): clarify allowlist URL and normalize IPv6 * fix(voice): harden NAT64 address validation * fix(voice): address managed endpoint review findings * refactor(voice): extract shared IPv6 transition unwrap ladder (#8350) Deduplicate the IPv6-transition unwrapping sequence (mapped, compatible, NAT64, dotted-quad) that was repeated verbatim between isPrivateNetworkIp and isAlwaysBlockedVoiceAddress on both CLI and Desktop surfaces. A single unwrapIpv6TransitionStep helper now yields the next canonical address (or 'blocked' for unrecognized ::ffff: forms), and each predicate recurses through it, preserving the exact re-check semantics at every unwrap level. * test(voice): cover allowInsecureBaseUrl wiring through desktop default transports (#8350) * fix(voice): add allowlist hint to private-network rejection error (#8350) * fix(voice): reject always-blocked base URLs before offering the allowlist hint (#8350) * fix(voice): resolve exact desktop voice provider before OAuth (#8350) * fix(voice): address review feedback for trusted private base URLs (#8350) * fix(voice): align desktop voice resolution with CLI semantics (#8350) * fix(voice): scope desktop fail-closed resolution to policy-bearing entries (#8350) * fix(voice): address round-8 review findings for trusted private base URLs (#8350) Run the invasive process-global `mock.module('ws')` suite as voice-ws-handler.isolated.ts so the desktop package's single-process `bun test` run no longer leaks the fake socket into unrelated ws consumers; the existing isolated loop runs it in its own process. Shape-guard the desktop provider scan: non-object modelProviders elements are skipped (falling through to OAuth instead of throwing a raw TypeError), and non-string baseUrl/envKey/settings.env values on a voice-model entry now surface the PROVIDER_ENTRY_REMEDY remediation error instead of crashing. Compute the DashScope-compatible /v1 rewrite before any allowlist match in fromExactModelProvider so the stage-1 check, the remediation messages, and the top-level recheck all compare the same final URL and a single allowlist entry converges for split-horizon deployments. Extend the CLI allowlist remediation messages to state which settings scopes honor the entry, since serve mode never shows the interactive workspace-strip warning. Thread providerProtocol through the CLI voice model seams (createVoiceModelSource and the daemon buildModelsConfig) so protocol-mapped custom provider groups resolve like the rest of the CLI model surface, and document the remaining protocol-agnostic desktop scan in the design doc. Correct the getHomeEnvFallback comment: it adopts the narrower getHomeEnvFallbackVars candidate set on purpose. Add multi-record DNS answer tests on both CLI and desktop net guards so the records.some classification is pinned against the array shape defaultLookupHost always produces in production. * fix(voice): address round-9 review findings for trusted private base URLs (#8350) * fix(voice): address round-10 review findings for trusted private base URLs (#8350) * fix(voice): classify desktop voice duplicates before ambiguity check (#8350) * fix(scripts): compare voice guard mirrors as parse trees (#8350) --------- Co-authored-by: rockybot2026 <265985139+rockybot2026@users.noreply.github.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-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
f4cf4268f5
|
fix(core): cap a streaming response's total lifetime, slim the review fan-out launch (#8602)
* fix(core): cap a streaming response's total lifetime The stream inactivity watchdog resets on every chunk, so a drip-fed stream — a gateway trickling keep-alive chunks, or a model crawling through an oversized single message — kept it alive forever while the message never completed: in issue #8597's CI review runs that meant 2.5-4.5 hours of silence ending in the outer timeout's kill. Add a per-request lifetime cap (default 15 min) that does not reset on chunk arrival. Tripping it throws a retryable ETIMEDOUT, so the existing transport-continuation recovery resumes a healthy generation the cap happened to cut. Config field streamMaxLifetimeMs; env knob QWEN_STREAM_MAX_LIFETIME_MS; 0 disables. * fix(review): carry findings lists as digest-named files, not inline agent-prompt folded the cumulative findings list into every printed verify/reverse-audit block. On a 12-14-auditor round that made the launch one 65-82 KB assistant message — the oversized single message whose stream generation never completed in #8597 — and cost a 5-10 minute paged relay through the orchestrator's context before every round. The list now goes where the brief already goes: on disk, named by the same findings digest that keys the record; the block carries a read_file pointer. The delivery guarantee is unchanged — a launch that drops the read matches no record — and the retirement scheduler's echo guard reads the list back from the file the prompt names, failing toward auditing when it is gone. * fix: address review feedback on stream guards and findings files * fix: address review feedback on findings delivery and stream guards The delivery floor counted only the brief's read receipt, so an instruction-skipping verifier could open its brief, skip the one instructed read of the findings file its block points at, and clear the gate having never seen the list it ruled on. The floor now extracts the findings pointer from the recorded prompt and requires a successful read of it — a new findings-unread delivery shape, with gap texts for the verify, reverse-audit, and combined steps. A failed findings-file write also speaks on stderr instead of silently pointing a whole round at a missing file. Stream guards: consult the lifetime deadline at the top of the loop, not only when the timer wins the race — a pre-buffered chunk resolves next() as a microtask and beats setTimeout(…, 0) every time. Merge the two ETIMEDOUT bypass blocks into one rule. New tests pin the idle-off + lifetime-on branch, the both-guards-0 disable (config and env), and the buffered-source shape. Also reword the prose sites that still described the inlined list. * fix(review): name the findings file in the verify/reverse-audit briefs The two briefs still told the agent the findings were "listed in the message that launched you", but since the list moved behind a read_file pointer they find only a pointer there. The agent reads its brief first (the block mandates it), so the stale text sent it looking for finding blocks in the launch message — the exact findings-unread shape the new delivery floor gates on. Point both briefs at the .findings.md file and the required read instead. * fix(review): address round-3 review — empty-round brief, guard hardening The b796708 brief fix stated unconditionally that the launch message points at a findings file, but an early reverse-audit round on a clean review names no file (the empty branch prints '## Nothing is confirmed yet' with no pointer) — the auditors were being sent to read a path their prompt does not contain. The reverse-audit brief now says the file is named when there is a list, and that an early round has none. Also from the round-3 review: - writeFindingsFile's failure diagnostic now uses writeStderrLineSafe: the catch exists to keep the build alive, so it must not throw out of it on EPIPE (qwen … | head). - The retirement echo-guard resolves the findings pointer from the CLI's own record (never the orchestrator's pasted copy), confined to the plan's record dir, memoized per round — an out-of-bounds or unreadable file degrades to the prompt, failing toward auditing. - Document the delivery floor's deliberate weakening: it proves the findings file was opened, not paged to completion (coverage.ts), and drop the last stale 'record folds the findings in' comment. - withStreamInactivityTimeout returns the source untouched when both guards are disabled, so the invariant survives a caller refactor (setTimeout(Infinity) would otherwise clamp to ~1ms). - settings.md + the config comment now say the stream guards are OpenAI-compatible-only, and that the 15-minute cap bounds a stream whose idle timeout was raised above it (raise or disable the cap to keep a longer window). * fix(review): address round-4 review — memo hygiene, read-visibility, naming The retirement echo-guard memoized a failed read's per-record fallback under the round's shared findings pointer, so one chunk's launch prompt would serve as a sibling chunk's findings list. Memoize only successful reads; a miss falls back to THIS record's own prompt, uncached. The findings block now prints the list's line count, so an agent whose read_file truncates can see it saw a fraction rather than the whole confirmed list — a visibility aid the delivery floor (which proves the file was opened, not paged) does not provide. Also from the round-4 review: rename withStreamInactivityTimeout to withStreamGuards (it now enforces two guards), correct the constants comment (the cap is measured from the stream's first iteration, not its first byte), and give the two stream-guard knobs a dedicated settings.md entry instead of burying them in the timeout paragraph. * fix(review): address round-5 review — deterministic record walk, accurate line count The cross-contamination regression test only discriminated under one readdir order: readRecordedPrompts walked the record dir in filesystem (filename-hash) order, so which chunk's fallback poisoned the shared memo depended on the walk. Sort the directory listing — a deterministic walk removes that whole class of order-sensitivity from a module that reasons per-record, not just from this test (a temporary revert of the memo fix confirms the test now fails against the buggy shape). findingsSection printed the line count of the TRIMMED findings body while writeFindingsFile writes the untrimmed content, so a list with leading/trailing blank lines got a label smaller than the file the agent actually reads — precisely the under-reading the count exists to make visible. Count the untrimmed content. * fix(core,review): charge the lifetime cap on upstream-wait, not delivery time Round-2's top-of-loop deadline check made the lifetime cap measure end-to-end delivery time: a healthy upstream that finished and buffered its chunks was cut for the CONSUMER's slowness (a paused IDE client, a big render), and a stream whose terminal done resolved at the boundary was converted into a retry. The cap is now charged on accumulated upstream-wait — the time the loop is blocked in await it.next() — so a buffered, already-complete stream always completes and only real upstream latency counts; the drip-fed never-completing stream spends exactly that time waiting, so #8597's shape is still caught. Also from the round-6 review: - Hoist the stream-guard error branch above the thinking-tag check: a drip-fed gateway cutting mid-<think> surfaced the guard's ETIMEDOUT as a PROTOCOL_TAG_LEAK and burned the tag-leak retry budget. - The findings line count drops the trailing newline's empty segment, so a 12-line list is not advertised as 13. - settings.md: the stream guards are env/config-only (no settings.json key); document that streamIdleTimeoutMs: 0 embedders now also need streamMaxLifetimeMs: 0 to fully opt out. - constants: a functionCall already streamed (the tool-heavy common case) recovers as a visible classified error, not a continuation. - coverage.ts: drop the stale 'four shapes' counts after Delivery grew a fifth; pipeline: dedupe the instanceof in the guard debug log; and correct the both-guards-off test's comment to pin the outcome, not the caller mechanism. * fix(core,review): monotonic guard clock, findings-write fallback, read-only floor Round-6 follow-ups the previous commit left open: - The stream guards accounted on Date.now(): an NTP step forward (or a long sleep) killed a healthy generation on the next iteration, and a backward step silently disabled the lifetime cap — the hang #8597 exists to bound. All guard accounting is now performance.now(); the setTimeout it races is monotonic too, so the two agree. Vitest fakes performance alongside the timers, and a new test A/B-verified against the wall-clock shape (it fails on Date.now() accounting). - A failed findings-file write returned the path anyway, pointing a whole 12-14-agent round at a file that does not exist — every agent burned its round, then the delivery floor failed it. writeFindingsFile now returns null on failure and findingsSection falls back to inlining the list (the pre-#8597 shape): the recorded prompt carries the list, the delivery check compares it verbatim, and the build stays alive. - The findings delivery floor matched the path in ANY successful tool call's serialized args, so a search_file_content or list_directory that merely named the file cleared it without reading a line. The transcript parser now records read_file calls apart, and the floor counts only those — a mention is not an open. * fix(core,review): round-7 leftovers — upstream-wait wording, key helper, test mock The round-6 error message and class doc still said 'total lifetime cap' and promised the continuation recovery unconditionally, and the wrap-site comment still read 'aborts at maxLifetimeMs from stream start' — all three now describe the upstream-wait semantics the guard actually implements, and the message names the wall clock separately so the two numbers reconcile. The round suffix baked into findings-role record keys and the findings file name was derived independently in three sites (findingsFileFor, runAllChunks, the single build) — a change to how a round is spelled would update two of three and silently fork the artifacts; roundPartOf spells it once. The writeFindingsFile test's module mock replaced all of stdioHelpers (three stubs, missing the other exports) and asserted mock.calls[0][0] with no mock reset — it now spreads importOriginal, clears mocks in beforeEach, and matches the stderr lines with stringContaining. * fix(review,core): round-3 follow-ups — conditional findings briefs, JSDoc placement, log labels The verify/reverse-audit briefs told the agent its findings live in the .findings.md file unconditionally, on the exact path (writeFindingsFile returning null -> findingsSection inlining the list) this PR added to work WITHOUT one; both now say 'when the message points at a findings file, read it; on the rare write-failure fallback the list is inlined in the message, read it there.' roundPartOf had been inserted between findingsFileFor's JSDoc and the function, orphaning the comment that records the one-file-per-round and null-means-inline contracts; the helper now sits above it. The guard debug log labelled the wall clock 'streamLifetimeMs' beside the cap; it is now 'wallClockMs' so the two numbers reconcile the same way the error message does. * fix(review): anchor the findings-pointer extraction to its emitted shape findingsPointerOf matched the FIRST read_file(file_path="….findings.md") anywhere in the recorded prompt. On the write-failure inline fallback the findings list occupies exactly the position the pointer would sit in, and a finding entry there can itself quote a read_file pointer of its own (a finding about this pipeline, which the harness produces when it reviews this repo). The loose match then extracted the quotation as the pointer and the readers diverged: coverage demanded a read of a path no agent was told to read (a spurious findings-unread on a run that is already degraded), and retirement, worse, confined-and-read an earlier round's file and flipped a just-filed finding to an echo, retiring a chunk that had just reported — the one direction the module's header commits to never failing. A quoted pointer inside a findings entry is indented or embedded in prose, so anchoring to a standalone read_file line removes it; the happy-path pointer is alone on its own line inside its fence. A test drives an inlined list containing a pointer-shaped line (fails under the old loose regex). --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
ec4f1e02e4
|
fix(cli): probe sandbox runtime before selecting it (#7734)
* fix(cli): probe sandbox runtime before selecting it Sandbox selection treated PATH presence as proof a runtime works, so an installed-but-unusable docker (daemon stopped, socket unreachable, user not in the docker group) was still selected and the podman branch below it became unreachable. Each candidate is now probed with `version` — the cheapest command that still contacts the daemon — and the first one that actually runs wins. When nothing usable is found, the error names the runtime that broke and quotes its failure instead of claiming nothing is installed. An explicit QWEN_SANDBOX choice is never silently redirected; it fails with the daemon error attached. sandbox-exec is not probed, being a kernel facility rather than a daemon client. Fixes #7732 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): attribute the sandbox command to its real source The probe failure hardcoded "(from QWEN_SANDBOX)", but an explicitly named command also arrives from --sandbox or tools.sandbox in settings. Naming the env var unconditionally sends a user who never set it looking in the wrong place — the same misdirection this change set out to remove. The parenthetical is now emitted only when the env var actually supplied the value, which also corrects the pre-existing "Missing sandbox command" message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): apply source-accurate attribution to the auto-detect errors too The explicit-string path stopped hardcoding QWEN_SANDBOX, but the auto-detect errors still did, so `qwen --sandbox` with a broken runtime pointed at an env var the user never set. Both auto-detect messages now name the env var only when it was what enabled sandboxing, and otherwise suggest --sandbox. Also lowers the probe timeout from 10s to 5s. Probes run sequentially, so the ceiling is paid once per wedged runtime; a healthy `docker version` answers in roughly 200-500ms, so 5s keeps an order of magnitude of headroom while halving the worst-case startup delay. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(cli): assert the sandbox probe argv and timeout The spawnSync stub routed on command name alone, so the probe arguments were never observed. Rewriting the probe to `docker --version` — which prints the client build without contacting the daemon, restoring the original defect — left all 12 tests green, as did deleting the timeout that bounds a wedged daemon. Validate both in the stub, so every probing test carries the check, and pin the argv at the fallback call site where the behavior is asserted. Reported by @wenshao in the mutation matrix on #7734 (M9, M8). * test(cli): cover the empty-output and timeout probe branches Two probe branches in probeSandboxCommand were unpinned, so a mutant in either survived the whole suite: - a non-zero exit with empty output relied on the synthesized-message fallback; dropping it made the probe return undefined and a broken runtime read as usable - the result.error branch that the probe timeout produces had no test; deleting it degraded the error text with the suite still green Each new test fails against its mutant and passes on the real code. Reported by @qwen-code /review on #7734. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(cli): isolate the sandbox probe in config precedence tests The image-precedence tests enable the sandbox and assert only which image wins. Since the runtime probe added here spawns a real `docker version` subprocess, and this file mocks command-exists but not child_process, the probe runs for real. On macOS the sandbox-exec branch returns before probing, so it passed there and on CI runners that have docker; on any other host without a running daemon getSandboxCommand throws and all four tests fail on image assertions they never reach. Mock the docker/podman `version` probe to report healthy, so selection is deterministic and the tests exercise image precedence on every platform. Every other spawnSync call stays real. Reported by @qwen-code /review on #7734. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): cache the sandbox probe and sanitize its error output loadSandboxConfig runs twice on a sandboxed startup, so every candidate was probed twice — the wedged-docker-then-podman fallback paid the 5s cap twice (~10s), which the PR description wrongly called "once per wedged runtime". Cache each command's probe outcome per process (with a test-only reset), mirroring the ripgrep health cache, so a runtime is contacted at most once. The probe also returned the runtime's stderr verbatim into FatalSandboxError messages, carrying ANSI/control bytes to the terminal. Strip them with the existing stripAnsiAndControl helper, whose own doc names this case. Both requested by @wenshao in the review on #7734 (items 1 and 3). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(cli): keep an all-control-character probe failure from reading as usable The sanitizer checked the failure line for emptiness before stripping, so a runtime whose stderr is only escape/control bytes stripped to '' — falsy — and the broken runtime was selected as usable, reintroducing the presence-vs- liveness bug through the sanitizer. Check emptiness after stripping and fall back to the synthesized message. Also drop the redundant `candidate !== 'sandbox-exec'` guard (sandbox-exec is only a candidate once its presence is confirmed), reword the all-broken hint to "try another installed runtime" since another may be installed but also broken, and add tests pinning the control-character failure and the first-of-several- broken diagnosis. Reported by @qwen-code /review on #7734. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
5173052e37
|
chore(release): v0.21.6 (#8598)
* chore(release): v0.21.6 * docs(changelog): sync for v0.21.6 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
2ad72fd5e9
|
fix(cli): Bound ACP textual tool-result payloads (#8450)
* fix(cli): Bound ACP textual tool-result payloads Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): Harden ACP text projection budgets Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * perf(cli): Cap multi-block ACP projection scans Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(cli): Cover ACP projection boundary guards Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
86a33777b3
|
feat(review): test the diff's reverse-dependency closure with disclosed caveats (#8490)
* feat(review): test the diff's reverse-dependency closure, fail open to the full suite
build-test tested only the workspaces the diff changed. That under-tests in
the one way a compile cannot catch: a behaviour change in core leaves every
dependent compiling and still fails their suites, so the break surfaces in a
consumer's tests or nowhere. The test phase now runs the changed workspaces
plus their transitive dependents (dependencies/devDependencies edges, the
same declared graph the build set already walks) - still a subset of the
build set, so every tested package was built with what it compiles against.
Scoping is an optimisation over the full suite, and every input that would
make the scoped set unsound now falls back to the repo's own full-suite
command (one root `npm test`; per-workspace suites when the root defines no
test script), never silently:
- a changed file outside every workspace (root scripts/, .github/, docs):
the test scripts themselves live in the root package.json, so no
per-workspace subset covers such a change;
- a workspace whose package.json does not parse: it is invisible to the
dependency graph, so the closure may be missing a real dependent;
- a closure past half the workspaces: not a meaningful narrowing, and the
root command brings the repo's own parallelism.
The report discloses the decision either way in a new `testScope` field
({ mode: "workspaces", workspaces: [...] } | { mode: "full", reason }), and
the ok-note names the scoped suites, so Agent 7 can state exactly what was
and was not run instead of implying the whole suite passed. Agent 7's brief
is updated to read it. Single-package repos keep a byte-identical report -
their one suite is their full suite, and no scoping decision happened.
On this monorepo the closure keeps the common cases scoped: a cli-only diff
runs 1 of 22 suites, a core diff runs its 7-package reverse closure.
The new lib/workspace-scope.ts carries the pure decision (resolveTestScope,
unparseableWorkspaceManifests); the reverse closure is extracted from
buildSetFor into reverseDependencyClosure so the built set and the tested
set cannot drift apart.
* fix(review): keep inert prose from forcing the full-suite fallback
The out-of-workspace fail-open was too blunt: it sent a README or LICENSE
edit to the full suite, spending minutes measuring nothing - prose cannot
fail a test, and "run everything" for a docs PR is the exact waste test
scoping exists to remove.
The scope decision now distinguishes the two kinds of outside file:
- inert prose (what the plan pipeline's own classifyPath calls docs, plus
the extensionless license family: LICENSE, COPYING, NOTICE and variants)
neither forces the full suite nor widens a scoped set. A diff of prose
alone obliges no test at all - the pre-scoping "nothing to run, and that
is a complete answer" behaviour, restored; prose riding along with one
workspace leaves the run scoped to that workspace.
- influential files (scripts/, .github/ workflows, root package.json and
other root config) still fail open to the full suite with the reason
recorded - the root package.json defines the test scripts themselves.
Reusing classifyPath rather than a second docs definition means the two
cannot drift, and inherits its safe bias: markdown outside the docs dirs
and the repo root stays influential (in-tree markdown can be executable
behaviour), erring toward running MORE tests. LICENSE.js stays influential
too - a name is only inert when nothing executes it.
* fix(cli): run the scoped closure with caveats instead of a full-suite fallback (#8490)
The full-suite fallback ran the repo root npm test, which cannot finish
inside a command deadline on a large monorepo (31 minutes in this repo's
CI against a 300s deadline) and fired for about a third of diffs, trading
a working scoped signal for a guaranteed timeout. Replace it: the scoped
set always runs, and every input that makes it possibly incomplete is
disclosed as a caveat on testScope instead.
Also close the silent-scope gaps the review found: manifests that parse
but lack a usable name now count as unreadable graph entries; symlinked
workspace members are visible to the graph; optionalDependencies are
reverse edges; a root suite that declares a workspace dependency runs as
a dependent; negation-excluded members earn no caveat; the half cap
counts testable suites only; build-only probes report no testScope; and
the docs-classified inert carve-out is gone (root AGENTS.md is load-
bearing for packages/cli's own suite) — only the license family stays
inert.
* fix(cli): address review round 2 on scoped build/test honesty
Resolve the round-2 findings on the scoped closure work:
- testScope is attached only once the scope executed; every early return
that ran zero test commands no longer carries it (R2-1).
- The more-than-half cap counts a participating root suite on both sides
(R2-2), and its witness is pinned in both directions (R2-13/24).
- Manifests whose body is the JSON literal `null` classify as skipped
instead of throwing past the try/catch (R2-3); test-plan honours the
skipped signal rather than ruling a false absence (R2-5).
- The single-root note no longer claims tests ran when the root defines
none (R2-11), and the scoped note no longer claims build-only
dependents were tested (R2-21).
- A negation only excludes a file when it excludes the owning member, so
a partial negation keeps the member's suite visible (R2-7); a
glob-claimed dir with no manifest self-discloses (R2-20).
- The build set and test closure are computed over one root-inclusive
graph so a member that depends on the root is built before it is
tested (R2-23); the dead single-root skipped reset is removed (R2-9).
- peerDependencies gains a witness (R2-17), the skipped docblock records
the per-shape truth (R2-18), and caveat substance is asserted in the
note, not just the label (R2-15).
* fix(cli): address review round 3 on scoped build/test graph honesty
Round-3 Critical findings, all probe-verified against the tree:
- R3-1: the build-only (merge-base probe) path excluded the root from the
scope graph, so the probe measured a different build set than the run it
baselines. The graph is now identical for probe and full run.
- R3-2: a root that joins the closure as a dependent ran `npm test` at the
root — which for a `--workspaces` fan-out script repeats the ENTIRE suite
inside one command deadline, the fallback this module refuses. A fan-out
root now leaves the executed set with a caveat; it stays in the graph.
- R3-3: root participation was gated on a TEST script, so a build-only root
vanished as a graph node and every dependent reached through the root's
name was silently dropped. The root now joins the graph whenever it is a
package with a build or test script; the dir->package map is built from
the scope graph so the root's own build runs too.
- R3-5: test-plan ruled `contradicted` ("script does not exist") from a
table that was silently incomplete for unmodeled globs (`packages/**`) and
`./`-prefixed globs. Unmodeled layouts now rule `unchecked`, and `./` is
normalized everywhere the walker reads globs.
- R3-6: a literal member listed BEFORE a `*` claiming its parent segment was
silently dropped (npm includes it under either order). It now lands in
`skipped`, so the broken-graph caveat fires and names it.
- R3-7: a negation excluding a NESTED member nulled ownership instead of
falling back to the surviving outer member, certifying "a complete answer"
over a suite that collects the file. Ownership now falls back through the
previous-owner chain.
Round-3 Suggestions: caveat test names the shadowed shape; half-cap comment
arithmetic fixed (R3-11); the three stale "subset of workspaces the diff
touched" prose spots now say "plus the workspaces that depend on them"
(R3-12); test-plan merges scripts of parseable-but-nameless manifests and
reserves `unchecked` for genuinely unreadable ones (R3-14); the single-root
build-only test witnesses the executed commands (R3-16).
218/218 across the four touched test files; full src/commands/review suite
1751 passed, 1 unrelated real-git flake that passes standalone; typecheck,
eslint --max-warnings 0, and prettier clean.
* fix(cli): address review round 4 on scoped build/test honesty
Findings from the round-4 review of ec7b202:
- F1 (whole-call budget): the closure's per-command deadlines SUM, and on
this repo a core diff's closure is 7 suites covering ~86% of test files —
past the 600s ceiling the brief welds on, where the outer kill discards
the report. The test loop now runs against a whole-call budget (--budget,
default 2x --timeout = 600s): when the next command's deadline would
cross it, the loop stops and names the suites that did not run, through
testScope.caveat (or the note for single-root repos). A partial report is
signal; a discarded one is the 71-timeouts failure this command exists
to end.
- F2 (name collision): the root and packages/cli share the name
@qwen-code/qwen-code on this very repo. The root now goes FIRST in the
scope graph so members win the name map — last-write-wins would have
resolved a dependent of the CLI package to the root and silently dropped
the member's dependents from the closure.
- F3 (-ws alias): rootTestFansOut now also matches npm's -ws/--ws
shorthands, while deliberately not matching -w/--workspace (singular).
- F4 (caveats compose): resolveTestScope disclosed at most two of five
possible caveats — skipped short-circuited the outside-file facts, and
any caveat suppressed the half-cap. All applicable caveats now compose,
strongest first, because "nothing is silent" means composing the
disclosures, not letting the first one hide the rest.
Smaller items from the same review:
- build/test commands shell-escape the workspace dir (PR-authored tree
input; $() and backticks stay live inside double quotes on POSIX).
- The ok-note counts the root separately instead of reporting "of 23
workspaces" on a 22-member repo.
- Dropped the pointless `const rootPackage = rootPkg` alias and the two
no-op buildSet filters; un-exported workspaceDirCandidates (internal
only); documented the previous-owner stack's contrived-ordering limit.
224/224 across the four touched test files; full src/commands/review suite
1757 passed (1 unrelated real-git flake that passes standalone); typecheck,
eslint --max-warnings 0, and prettier clean.
* fix(cli): address review round 5 on budget-stop honesty and root builds
Blocking and high findings from the round-5 review of 0c0e907:
- F1 (blocking): a budget stop left `testScope.workspaces` claiming suites
ran that never did — the note said "Everything passed" two clauses from
the caveat naming them not-run. The trim is now structural: `workspaces`
is exactly the suites that ran, a new `notRun` field names the trimmed
ones, and the zero-ran note branch says the budget was spent instead of
"no workspace defines a test script".
- F2 (blocking): budget truncation was alphabetical, so the CHANGED
workspace's own suite could be the one dropped (a `zebra` change ran
`alpha`'s suite first). The test loop now runs affected workspaces first;
the dependents are the widening, and the widening is what a budget trims.
- F3 (high): a root that devDepends on a workspace put `.` in the build
set and ran a bare `npm run build` — the whole-monorepo build this module
exists to stop. A fan-out root build (`--workspaces`/`-ws`/`--ws`) is now
skipped like its test counterpart: an aggregator produces no artifacts of
its own, and the scoped loop already builds the members it drives.
`rootTestFansOut` generalized to `rootScriptFansOut(root, script)`.
Calibration and nits:
- F4: the budget default keeps 30s of headroom under the 600s tool ceiling
(the clock outside starts before node does), floored at one per-command
deadline so a tiny --timeout cannot go negative; option and docblock now
say the budget covers the whole call, install included.
- F5: the half-cap caveat no longer says "of the suite" — it counts
workspaces and now says exactly that.
- F6: shellArg's docblock scopes the claim to POSIX (cmd.exe is not a
sealed surface); the "every return above ran zero test commands" comment
no longer contradicts the nothing-to-run early return; failure notes
carry the caveat too (the note is what the brief renders first); and the
root docs/ tree is carved out of the influential set — a caveat that
fires on most PRs teaches the reader to ignore caveats, while root-level
prose (AGENTS.md, asserted on by load-rules.test.ts) stays influential.
229/229 across the four touched test files; full src/commands/review suite
1762 passed (1 unrelated real-git flake that passes standalone); typecheck,
eslint --max-warnings 0, and prettier clean.
* fix(cli): address review round 6 on budget admission and report honesty
Findings from the round-6 review of 17209f09:
- F1 (medium, efficacy): the budget guard reserved a full --timeout per
suite, so at shipped defaults (300s deadline, 570s budget) at most one
test command could ever run after install+build — the headline coverage
was inert exactly where it mattered. The loop now ATTEMPTS every suite
with whatever of the budget remains, up to its own deadline: a suite
killed at the boundary is a timeout (already framed as infrastructure),
and only suites never attempted are named notRun. A partial attempt is
signal; a skipped suite was none.
- F2 (low-medium): results.buildSet kept '.' when the fan-out root build
was skipped — the same class of defect round 5 fixed structurally for
testScope.workspaces. The reported set is now filtered to what was
actually (to be) built.
- F3 (low): rootScriptFansOut classified `--workspaces=false` — an
explicit opt-OUT — as a fan-out, because `\b` matches before `=`. The
flag must now stand alone: `--workspaces(?=\s|$)`.
- F4 (low): testScope.notRun had no consumer — the agent's brief now
names it: suites the budget stopped before they ran are stated as not
run, never folded into the coverage.
F5 (half-cap caveat meaning) and F6 (ok:true on a zero-suite run) are
answered without code change in the PR reply: the half-cap sentence tells
the agent not to oversell the scoping, and ok reflects failures, never
coverage — both fully disclosed.
229/229 across the four touched test files (the budget witness now uses
real wall clock); full src/commands/review suite 1988 passed (1 unrelated
real-git flake that passes standalone); typecheck, eslint
--max-warnings 0, and prettier clean.
* fix(cli): address review round 7 on budget coverage and caveat calibration
Findings from the round-7 review of 00556278:
- R7-1: the whole-call budget guarded only the test loop — a 300s install
plus one 300s build already reached the 600s tool ceiling before any
suite was attempted, discarding the report the budget exists to save.
Install and every build now spend from the same budget (each command gets
min(its deadline, what remains)). A build phase the budget cuts short is
disclosed as `notBuilt`, filtered from the reported buildSet, and suites
of unbuilt packages (plus their dependents, via the closure) are not run
— running them would manufacture failures the diff did not cause.
- R7-2: a near-zero remaining slice manufactured a fake timeout (npm cannot
boot in 200ms: exitCode null, ok flips false, the suite absent from
notRun). A 15s attempt floor now routes those suites to notRun instead,
and the timeout notes interpolate the deadline the command was actually
given (CommandResult.deadlineMs), not the flag default.
- R7-3: the outside-file caveat fired on 38% of recent commits (measured)
— .github/**, CHANGELOG.md, and editor/VCS dotfiles earn no caveat now
(no workspace suite reads them, and the workflow tests that do live
outside the npm workspaces either way). Same cry-wolf argument the docs/
carve-out made, now with numbers.
- R7-4 (sizing): explicitly accepted — the PR body now states the cost
honestly: on the most common closure (7 suites incl. core+cli, ~56% of
workspace-touching commits) the 570s default realistically fits the
affected suite plus a dependent or two, the rest disclosed in notRun.
Smaller items:
- A diff inside a negated member (!packages/desktop) no longer reads as a
complete answer: the softest caveat says its own toolchain's suite was
not run — without claiming the scope is incomplete.
- scriptFansOut is pure on the script text; readRootPackage returns
RootPackage with scriptsText, ending the manifest re-reads.
- One named predicate (rootBuildSkipped) drives both the build-loop skip
and the buildSet filter, fixing the singleRoot mismatch.
- TestScope.workspaces' docblock now says the order is scope order, not
run order (test[] records execution order).
- New witnesses: the all-notRun note branch, notBuilt/notRun below the
floor, the negated-member soft disclosure, the CI/changelog carve-out.
230/230 across the four touched test files; full src/commands/review suite
1989 passed (1 unrelated real-git flake that passes standalone); typecheck,
eslint --max-warnings 0, and prettier clean.
* fix(cli): address review round 8 — structural notBuilt for base-tree
Findings from the round-8 re-review of 0f1492f9:
- R8-1 (the one that mattered): a budget-truncated BUILD passed base-tree's
availability gate — ok:true, toolchain npm, build[] non-empty — and got
the success marker, so test-delta would rerun the PR's failing files
against a base whose packages were never compiled; those reruns fail and
land in `shared`, and "shared = pre-existing by measurement, never filed"
waves a real regression through with the confidence of an A/B. notBuilt
is now structural on BuildTestReport, and base-tree treats a non-empty
one as unavailable — writing NO marker (truncation is a budget artifact,
not a settled answer about the SHA), so a later shard may repay and
succeed.
- R8-2 (carried over): the aggregate test-timeout note still quoted
--timeout; it now interpolates the deadline the command was actually
given, like the install and build-failure notes already did.
Nits:
- install gets the same 15s attempt floor as the build/test loops — a
sub-second `npm ci` would only manufacture a fake timeout, so it is
skipped and disclosed.
- notRun is sorted (both scope fields stable and comparable).
- rootSuffix now keys on the unified rootBuildSkipped predicate, and a
single-root repo counts its one package as the one workspace again
("Built 1 of 1", not "0 of 1 (plus the root package)").
- The 中文说明 block in the PR body is rewritten to match the English
(docs/CI carve-outs, all-phase budget, notRun/notBuilt, current counts).
246/246 across the five touched test files (incl. a base-tree witness:
truncated build → unavailable, neither marker written, next shard repays);
typecheck, eslint --max-warnings 0, and prettier clean.
---------
Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
|
||
|
|
477859bb3f
|
feat(channels): support local gh authentication (#8461)
* feat(channels): support local gh authentication Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(channels): align registry catalog test and visuals with optional GitHub token (#8461) * fix(channels): address review feedback for GitHub local gh auth (#8461) Treat a blank replacement of an optional secret as a clear so an existing GitHub channel can no longer ship an empty or whitespace-only PAT to the daemon. Reuse the shared missing-field predicate in the editor's GitHub credential validation, wrap malformed baseUrl failures in an actionable channel error, and surface sanitized gh stderr in local authentication failures. * fix(channels): address second-round review feedback for GitHub local gh auth (#8461) Pin the whitespace-only token gate, the bounded gh stderr sanitization, and the required-secret blank-replacement guard with mutation-resistant tests. Log the authenticated account identity on channel connect so an out-of-band gh auth switch is visible to operators. Align test secret-source fixtures with the SDK union and complete the design doc's change footprint. * fix(channels): address third-round review feedback for GitHub local gh auth (#8461) * fix(channels): address fourth-round review feedback for GitHub local gh auth (#8461) * fix(channels): address fifth-round review feedback for GitHub local gh auth (#8461) * fix(channels): address sixth-round review feedback for GitHub local gh auth (#8461) * fix(channels): address seventh-round review feedback for GitHub local gh auth (#8461) * fix(channels): address eighth-round review feedback for GitHub local gh auth (#8461) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.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@service.alibaba.com> |
||
|
|
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>
|
||
|
|
a5c637b749
|
feat(web-shell): add native Live Voice (#7859)
* feat(web-shell): add native Live Voice * fix(web-shell): address review feedback for Live Voice PR (#7859) - Quote all strings in electron-builder.yml to fix yamllint CI failure - Gate discovery publish on liveVoiceEnabledAtBoot to avoid writing bearer token to disk when Live Voice is disabled (M1) - Add child identity guard to CommandMonitor stdout/stderr handlers to prevent stale helper output from corrupting the new buffer (M4) - Add exponential backoff to sent-completion delivery retry (M3) - Skip broadcastState when setCallState/setTranscript value is unchanged to reduce per-audio-delta overhead (H1) - Document sent-mode completion notification in module docstring (H2) - Remove dead protocol/nonce aliases from readDiscoveryFile - Fix single instance lock fall-through with process.exit(0) * fix(cli): register realtime_voice in docs contract and env guard (#7859) * fix(web-shell): address review feedback for Live Voice PR (#7859) * fix(cli): discard orphaned isolated dir when parent restore fails (#7859) * fix(web-shell): address review feedback for Live Voice PR (#7859) * fix(serve): harden live turn recovery * fix(desktop): restore Live Host native build * fix(live): align native host and session isolation * fix(acp): preserve live worker continuation lineage * fix(live): classify provider close reasons * fix(serve): discard unused recovered conversation dirs * fix(live): isolate authorized realtime responses * fix(live): preserve realtime response authority * feat(web-shell): complete Live Voice onboarding * fix(live): persist realtime-owned dialogue * fix(live): preserve final speech while stopping * Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)" This reverts commit 7110bec6b034c702bca6e28e35b93c7f70e729cd. * Revert "fix(cli): discard orphaned isolated dir when parent restore fails (#7859)" This reverts commit 85165f1b2ddfaa311b8be91acdd76a6f388f6204. * Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)" This reverts commit 9199fa633e102bb8f24e4b216d322be4323eb3fc. * Revert "fix(cli): register realtime_voice in docs contract and env guard (#7859)" This reverts commit 6b6b1718352ef01a98a73976b5c7c4433fd14c35. * Revert "fix(web-shell): address review feedback for Live Voice PR (#7859)" This reverts commit e083779105199d26de3afd8ad00719a08efe3099. * revert(live): remove remaining takeover behavior * revert(live): restore pre-rollback implementation * test(cli): align Live diagnostics env guard * test(release): cover Live Host publication * fix(ci): re-sign Live Host package before verification * fix(serve): scope sent completion notifications to Live * fix(web-shell): preserve live setup errors * fix(live): align realtime backend speech lifecycle * ci(live): publish Live Host independently * test(cli): mock Live speech bridge handler * test(release): align Live Host workflow contract * fix(live): address release and lifecycle review findings * fix(live): release completed call tracking --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
6d4d9b5238
|
perf(review): retire dry chunks and pipeline verification in the reverse audit (#8498)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 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
* feat(review): retire dry chunks from reverse-audit rounds and pipeline verification Rebuild of the retirement/pipelining feature as one commit on top of the merged reverse-audit budget gate (#8468). - retirement.ts: per-chunk scheduler over the CLI's own prompt records and the harness transcripts — a chunk whose two most recent audits are substantive dry receipts is cold-checked on the even rounds instead of audited on every one, and a cold check that yields returns it to every-round auditing. The certifying match is counted records per transcript (one launch matching several records certifies none), dry receipts are read structurally with the zh forms beside the English ones, and a filed finding requires the File+Severity pair so an echoed quotation cannot pin a chunk hot. Everything fails toward auditing. - agent-prompt --all-chunks: requireAuditableChunks, then the schedule (round >= 3, fail-open to all-due on any error), then CONVERGED exit 5 (nothing built, no stamp, no marker), then the budget gate (exit 4 + marker), then the build. The admission stamp keeps the #8468 ordering and lands only after the build succeeds: a cold-check-only round that builds still stamps, a converged round never does, and a build that throws leaves no stamp. - agent-prompt --chunk: a round holding an admission stamp is repaired without gates or scheduling; an unadmitted round answers to the same sequence as --all-chunks (convergence, then budget), and its first chunk build is the round's admission — stamped after the build. - prompt-record: optional sinceMs fence on readRecordedPrompts (history readers only; coverage's obligation reads stay unfenced), plus the flattenPrompt/deliveredVerbatim split so the scheduler flattens each launch once instead of once per (record, transcript) pair. - deadline: doc-comments rewritten for the pipelined cadence — the admission-to-admission measure no longer contains a verification pass, so the tail reserve is the terminal round's only cover (replacing the 'deliberate margin' overlap rationale), and the workflow's reserve cap is cross-referenced. runEpochMs fencing, the bilingual budget-stop marker and the stamp semantics are unchanged from #8468. - SKILL Step 5: builder-owned 3B scheduling, the CONVERGED exit-5 termination rule, verification launched alongside the next round's auditors, and the cumulative reported list with '— [unverified]' tagging (added at the admitting merge, cleared or removed after the verdict; anything still tagged is excluded from Step 6). Superseded pieces of the parallel branch were dropped in favour of the #8468 form now on main: the planMtimeMs-equality fence (runEpochMs stays), the budget-scaled round-1 estimate, stamping inside the admission helper, and the branch's variants of the budget-gate tests. * test(review): split the tool-call guards in the retirement classifier tests Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): harden the retirement classifier and pin its gate orderings (#8498) Review-round fixes for the per-chunk retirement feature: - retirement.ts: admit the full-width colon (U+FF1A) in zh dry receipts, which the old [::] class silently refused; refuse the brief's own example receipt and prose artifacts (a stray backtick, the conjunction "and/or") in the substance check; require the transcript's diff reads to overlap the chunk's baked territory before a dry receipt counts; stop counting a quoted cumulative-list entry — a File+Severity block whose file line appears verbatim in the agent's own launch prompt — as a filed finding. Every change fails toward auditing. - agent-briefs.ts: export the reverse-audit example receipt (REVERSE_AUDIT_EXAMPLE_RECEIPT) and interpolate it in the brief, so the brief and the classifier's parrot refusal cannot drift. - Tests: rewrite the transcript-fence test, which a future-dated plan made vacuous, and add probe-verified pins for every fix plus the previously unpinned guards — converged-before-budget under deadline on the --chunk path, the --chunk transcripts-unavailable degrade, the stamp-keyed repair exemption (records without a stamp stay refused), budget-stop marker absence on the admission side, uncertified cold-check recovery, the history-less chunk guard, and the same-round multi-record merge in both digest orders. - SKILL.md: schedule the Step 5 findings merge unconditionally — verdicts land on dry rounds too — and cap a would-be Approve at COMMENT when a reverse-audit entry's verifier never ruled on it. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): bind the retirement territory to the diff read and reprice the budget gate (#8498) * fix(review): machine-check the unverified-tag backstop and reprice the tail reserve (#8498) --------- Co-authored-by: verify <verify@local> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
8b0e8b8192
|
fix: add onCompromised handlers to proper-lockfile calls to prevent daemon crash (#8442)
* fix: add onCompromised handlers to proper-lockfile calls to prevent daemon crash When a lock.lock directory is deleted while held (e.g. by another process cleaning stale locks), proper-lockfile's updateLock timer gets ENOENT on stat and calls the default onCompromised handler, which throws and crashes the process. Add onCompromised handlers that log instead of throw, consistent with existing handlers in tasks.ts and mailbox.ts. * fix: guard lock release after compromise and cover trusted folders (#8442) * fix: guard mailbox/task lock release and cover compromise handlers with tests (#8442) * test(core): add lock-compromise regression tests for mailbox and task guards (#8442) * test: share lock-compromise simulation via test-utils helpers (#8442) --------- Co-authored-by: 易良 <1204183885@qq.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
e34780e24d
|
fix(ci): clean review worktrees after cancellation (#8474)
* fix(ci): clean review worktrees after cancellation * fix(ci): remove orphaned review worktree directories * fix(tests): sync qwen-resolve-workflow expectations with externalized review timeouts (#8474) * fix(ci): pin review worktree cleanup patterns to paths.ts (#8474) * fix(ci): harden review cleanup sweeps and cover integration_cli (#8474) * fix(ci): extend review cleanup sweep to web_shell_e2e_smoke (#8474) * fix(ci): harden review cleanup git calls * fix(ci): tighten review cleanup comments and test guards (#8474) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(ci): pin review cleanup recipe copies byte-identical (#8474) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(ci): guard review worktree removal and pin cleanup invariants (#8474) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
1a2bb10298
|
fix(review): admit evidence images by content, not by name — magic-byte sniffing (#8459)
* fix(review): admit evidence images by content, not by name — magic-byte sniffing The publish-assets allowlist was extension-based, and an extension is a claim anyone can make: combined with a prompt-injected review run, whatever could name a file evidence.png could host up to the size cap of arbitrary bytes at a github.com URL through the evidence push (raised in #8454's review as an enablement consideration). sniffImageFormat reads the four admitted signatures (PNG, JPEG, GIF87a/89a, RIFF+WEBP — RIFF alone is not enough, AVI and WAV share the container prefix) and validateAssetContent rules the content against the format the extension claims, fail-closed: an unrecognized signature refuses even when the extension is allowed. publish-assets applies the ruling to every file's first bytes before anything is uploaded — all-or-nothing, same refusal contract as the other gates. Pinned: the four signatures, truncated/empty headers, the RIFF/AVI near-miss, extension-format mismatch, fail-closed unknown extensions, and end-to-end: a shell script named evidence.png refuses with exit 3 and nothing pushed. * fix(ci): re-pin review timeout tests to vars externalization (#8459) * fix(review): single-source the asset allowlist and pin sniff depth (#8459) Address round-1 review suggestions: - ASSET_EXTENSIONS now derives from EXTENSION_FORMAT, so admitting a format is a one-place change the batch gate and the content gate cannot drift on; the duplicated extension extraction moves into one claimedExtension helper shared by both gates. - Pin what the mutation probes showed unpinned: the GIF87a branch (sniff + admission), uppercase extensions at the content gate, the refusal message direction, the full depth of every signature (near-miss negatives), and the publish-time 16-byte slice end to end (a WEBP publishes through runPublishAssets; verified the test fails when the slice shrinks to 8). * fix(review): pin every sniff check and guard the allowlist lookup (#8459) * fix(review): pin every sniff byte and the two-gate format coupling (#8459) * fix(review): align the two-gates comment with the pin that enforces it (#8459) * fix(review): sharpen evidence-gate diagnostics and pins (#8459) - Content refusals now name the JSON-quoted full path, so two same-named files from different directories are tellable apart (the sibling read-error refusal already spent the path). - Export ASSET_HEADER_BYTES from the lib that owns the sniff depth; the publish call site uses it and the two-gates pin slices canonical headers to it, so a future longer signature fails the pin, not real publishes. - One shared refusal builder keeps the allowlist message identical in validateAssetFile and validateAssetContent. - Narrow the sniffImageFormat threat model to what magic bytes buy: binds the claimed type to the leading bytes, does not stop prefixed payloads. - One-byte-off matrix becomes a labeled it.each table (failures name the exact corrupted byte); imports re-alphabetized. * test(review): pin the shared extension refusal across both asset gates (#8459) * test(review): pin lastIndexOf extension parsing for multi-dot asset names (#8459) * fix(review): admit WEBP by its fourcc and name refused files once (#8459) --------- 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@service.alibaba.com> |
||
|
|
4f79036a22
|
feat(review): a cost ledger from the records already on disk (#8471)
* feat(review): a cost ledger from the records already on disk "0.21.3 was fine, 0.21.4 got slow" was settled only by replaying a whole review under a telemetry exporter and hand-aggregating the output — hours of forensics to find a repair round that had silently doubled a run (measured: a +93/-48 PR at high effort cost 523 model calls and 37.8M input tokens, 9.7M of them redelivering prompts the agents had already acted on). The usage data was on disk the whole time: every chat and subagent transcript event carries usageMetadata. qwen review cost-ledger --plan <plan> aggregates those records — the same files check-coverage trusts for delivery, found via the same environment-exported location, floored at the plan's mtime so a review started an hour into a session does not bill that hour — into per-stream totals: the main loop and each agent, with input / cached / output / thinking counts and wall time. Step 8 pastes the printed block into the saved report, so the next slowness question is a diff of two archives instead of an excavation. Informational by construction: an incomputable ledger prints why and exits 0. Validated against the measured run above — 521 calls, 37.7M input (93% cached), 849k output, 107 min — matching the telemetry-side aggregation, minus the two side-query calls that are not the review's. * test: register cost-ledger in the subcommand registry and its demand message * fix(review): honest cost-ledger output and a safe archive write (#8471) Address the review of the cost ledger: report output tokens once (thinking is a subset of candidates, not a sibling), keep the --out write inside the exit-0 contract and mkdir its parent, name a missing plan as the plan, read each transcript once, compare timestamps as instants, fold relaunched agents into marked rows, and archive the full ledger next to the Step 8 report. * fix(review): cost ledger — honest failures, validated plan, shared records (#8471) Address the second review round: a missing or faulted chat transcript now says "cost-ledger unavailable" instead of rendering agents-only totals as the whole cost (the plan proves the main loop ran), and a subagent dir that fails listing with anything but ENOENT does the same. The --plan file is validated as a plan report before its mtime alone sets the billing window. Output derives from totalTokenCount − promptTokenCount when present, correct under both usage conventions. Chunk agents label "chunk N" via the shared CHUNK_RE instead of the malformed "agent chunk N of M"; the transcript listing is one helper shared with the coverage gate; glued JSONL records are recovered via parseLineTolerant; totals reuse the rows' accumulator; folded (×N) rows rank by combined total; stale agent files are skipped by mtime without being opened. Rendered block gains pluralization, "agent runs: N", and a B tier; SKILL.md states the ledger's bounded window. * fix(review): close the cost-ledger audit — refusals, labels, pinned math Address the remaining review threads on the cost ledger: - Refuse agents-only totals when the chat file exists but holds no above-floor records: a degraded recorder leaves the file present and empty while agents run — the same infrastructure fact as an unreadable transcript, and exactly the output the missing-file refusal exists to prevent. - Read the agent label from the first user record, not a raw 64KB head slice: a fork's agent_bootstrap record precedes the launch prompt, quotes other agents' identity lines, and can outgrow any fixed window. - Distinguish parallel invariant agents by their owned file, so per-file runs stop folding into a phantom (xN) relaunch row. - Coerce negative provider counts to zero: the agent path records usage uncoerced, and summed negatives rendered >100% cached shares. - Accept degraded diff-less Step 1 reports: validate diffLines + chunks, the pair every plan report carries, instead of check-coverage's stricter contract that refused them. - Pin every branch the second round proved unobservable: the exit code on all handler paths, total - prompt under both usage conventions, per-agent fault tolerance, the wall-minutes conversion, array-shaped usage, the mtime pre-filter and the event-level floor, human() rounding, per-condition plan validation, sort order against a lexical readdir, the zero-event skip, the --out per-stream archive contract, error messages naming their paths, truncation membership and folded-run counting, and the assistant-type filter. Every new assertion was mutation-probed: each mutant the review named now turns the suite red. * review: pipeline stages keep their own ledger rows The (×N) fold keyed on the label alone, and three legitimate multi-launch shapes shared one: a reverse-audit chunk auditor is launched with the same 'chunk N of M' identity as the Step 3B territory finder (five audit rounds folded into the finder's row — one agent where six pipeline stages ran), and repeat rounds of the findings roles carry their round OUTSIDE the backticks (every round folded as a phantom relaunch). labelOf now reads the stage from the audit brief's record key in the launch (audit chunk N (round K)) and the round from the identity LINE — never the whole launch, whose folded findings can quote a budget disclosure's own '(round N)' — so rounds are rows and only true relaunches and same-round verify shards fold. The (×N) comment now says what the marker means: N runs under one label. * fix(cli): annotate cost-ledger test helper to restore strict build (#8471) * fix(cli): anchor cost-ledger labels and harden broken-usage defenses (#8471) --------- Co-authored-by: verify <verify@local> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
32e2741577
|
perf(core): clear tool results to a low watermark to preserve prompt cache (#8464)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 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
* perf(core): clear tool results to a low watermark to preserve prompt cache Size-triggered microcompaction now clears oldest compactable tool results down to half the threshold instead of stopping just below it, so the conversation prefix stays stable between clearings and provider prompt caches keep matching. The recent-result budget now protects committed results only; pending results no longer consume protection slots but stay counted, uncleared, and live for file-read-cache resolution. Adds the watermark to cleanup metadata and the debug log. Fixes #8463 * fix(core): harden size-cleanup protection against zero-char and pending refs Review follow-up for the low-watermark change: keepRecent now selects from committed results that are actually clearable (positive, successful, uncleared output), so trailing errors, prior placeholders, and empty outputs no longer absorb protection slots. Pending refs are dropped from the keep set entirely — a pending read may be a cache-hit placeholder rather than file bytes, so it must not suppress eviction reporting; over-disarming only costs a redundant re-read. Adds regression tests for both plus the protected-saturation consecutive-trigger corner. * qwen: address PR review feedback (#8464) Pin the (soft-exceeded) log marker with the one-line assertion suggested by the sandboxed verification report (finding S-1): the all-protected overage test now asserts 'target 250000 (soft-exceeded)', killing the surviving mutant M4. * qwen: address PR review feedback (#8464) * qwen: address PR review feedback (#8464) * qwen: address PR review feedback (#8464) Two P1 context-integrity fixes from review: (1) media-only tool results (image/PDF reads with empty text output and bytes on functionResponse.parts) stay in the idle-path keepRecent candidates instead of being dropped by the zero-char filter; (2) only write_file results vouch for file residency in kept-path accounting — edit calls carry just old/new snippets while still setting the cache's sticky full-read flags, so a kept edit can no longer suppress eviction reporting after the full read is blanked. Regression tests for both. * qwen: address PR review feedback (#8464) Pin the absence of the (soft-exceeded) marker at the exact watermark boundary: clearing that lands the virtual total exactly on the watermark must not be flagged. Kills the >= and always-true mutants of the marker condition that previously survived the suite. * qwen: address PR review feedback (#8464) --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
2bd5c94111
|
fix(serve): detect lineEnding across the file, not the returned slice (#8383)
* fix(serve): detect lineEnding across the file, not the returned slice `readText` reported `meta.lineEnding` from the slice it was about to return. A slice holding a single CRLF line arrives as text ending in '\r' — the '\n' was consumed as that line's terminator — so detecting on it answers 'lf'. Page one of a cursor sequence then disagreed with page two about the same file, and a client that trusts the first page would rewrite CRLF content as LF. The truncation branch re-detected on the truncated slice for the same reason and had the same flaw. Detect on the whole decoded file once, which is what the field is describing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(serve): guard byte-truncated reads against slice-based lineEnding re-detection * fix(core): report crlf on cursor pages resuming after a CRLF terminator (#8383) * fix(core): count a skipped CRLF terminator on byte-truncated pages (#8383) When a window's first line exceeds both the read-chunk size and maxOutputBytes, the byte cut fires before the line's terminator is decoded, and the re-snap then walks over that terminator without reading it. The next page seeds from the pair and reports 'crlf' while the cut page reported 'lf' — adjacent pages of one file disagreeing, the exact symptom this PR removes. Consume the same two-byte evidence after the re-snap so the pages agree. Also qualify the design-doc agreement guarantee to files with uniform line endings (mixed-ending files can still flip between pages), and pin the seed's load-bearing placement with tests: it must run on the snapped offset, and the minimum probe offset (startOffset == 2) is now covered. * docs: correct the lineEnding spec for byte-cursor pages (#8383) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: qualify the mixed-EOL verification bullet for byte-cursor pages (#8383) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * docs: name the uniform-file line-window-vs-cursor lineEnding split (#8383) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.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> |
||
|
|
3ad4fbdb7d
|
fix(cli): preserve Qwen Review startup version in footers (#8431)
* fix(cli): preserve review startup version in footers * fix(cli): keep review startup version dynamic in bundle * fix(cli): reset review version after managed update * test(cli): use indexed env access * fix(cli): harden review footer strip and version stamping (#8431) * fix(tests): sync qwen-resolve-workflow expectations with externalized review timeouts (#8431) The timeout externalization in #8460 replaced the hardcoded 300/240 values in qwen-code-pr-review.yml with the QWEN_REVIEW_JOB_TIMEOUT_MINUTES and QWEN_REVIEW_MAX_TIMEOUT_MINUTES repository variables but left scripts/tests/qwen-resolve-workflow.test.js asserting the old literals, so the full-profile Test job fails on any branch carrying that change. Update the three affected assertions to pin the externalized shape. * fix(cli): harden the review footer strip per review feedback (#8431) The strip regex kept a 2^(N-1) partition ambiguity for same-line footer runs (measured 5.3 s at n=20) and missed footers truncated before their closing `_`; forged footers also survived on the body channel through `bodyCriticals`, and the values interpolated into the footer were not shape-validated. Guard the repeated group so an iteration cannot span another footer's start, make the final `_` optional, strip body Criticals per entry, refuse footer-forging model ids and non-version stamps, refuse non-object comment entries, pin the CLI-glue test suite against an ambient startup stamp, and cross-assert the LGTM filter regex against the footer builder. * fix(cli): strip review footers before ledger carryover * fix(cli): align ledger footer regression expectation --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
5631f4b112
|
feat(serve): add a required external tool guard provider (#8125)
* feat(serve): add required external tool guard * fix(serve): keep guard constants off fast path closure * test(serve): cover guard startup options * refactor(acp-bridge): centralize external tool guard validation and ack value (#8125) * fix(core): align MCP reconnect timeout test with safe replay policy (#8125) The reconnect-on-timeout test still built its mock tools without server trust or tool annotations, which the safe replay change now requires before automatically replaying a connection-loss failure. Update the fixtures the same way the surrounding reconnect tests were updated, keeping the test's original assertion that a timeout on a known disconnected server goes through the reconnect path. Mirrors the same alignment already landed on main. * fix(cli): alias externalToolGuard subpath for vitest source resolution (#8125) This PR added `@qwen-code/acp-bridge/externalToolGuard` imports to cli serve/acp modules but not the vitest source alias every other acp-bridge subpath carries. Without it, any vitest run whose acp-bridge dist is stale or absent fails to resolve the import and the five serve test files die at transform time. Add the alias following the documented convention in the config so tests read the live source. * fix(serve): reject non-ASCII external tool guard bearer tokens A token outside the ASCII range passed construction but made the handshake throw ERR_INVALID_CHAR when interpolated into the Authorization header, blocking qwen serve startup in required mode with an unexplained error. Enforce printable ASCII (0x21-0x7E) at validation time so the configuration fails fast with a clear message. --------- Co-authored-by: 秦奇 <gary.gq@alibaba-inc.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-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
eacc85e846
|
fix(cli): stop review test-efficacy tests depending on ambient tmpdir vitest (#8537)
Two tests failed on hosts where vitest resolves up-tree from os.tmpdir() (observed on self-hosted CI where a node_modules above TMPDIR provides one): findVitestBin's "cannot be resolved" case never threw, and runControlMutant's "cannot run" case executed the probe for real instead of throwing. Make the failure conditions host-deterministic while keeping every assertion: findVitestBin accepts an injected resolver (default unchanged) so the MODULE_NOT_FOUND case is forced directly, and the runControlMutant test plants a shadow vitest whose exports hide package.json, which wins resolution from any ancestor install and makes the run fail deterministically. |
||
|
|
52e0d1b364
|
feat(web-shell): bind plan approval to its Todo revision (#8393)
* feat(web-shell): gate session workflow behind experimental setting * feat(web-shell): bind plan approval to todo revision * fix(cli): clear stale workflow revision on plan entry * test(web-shell): pin revised workflow snapshot * fix(cli): clear stale plan revisions on restore * fix(cli): keep replayed history from rebinding plan revisions History replay re-sends stale plan updates through Session.sendUpdate, re-stamping activeTodoPlanRevision from finished plan cycles. Clear the revision after every replay path (cold replayHistory and live non-bulk loadSession) so a replayed snapshot can never bind a later exit_plan_mode approval; reloaded sessions fall back to text-only approval until the next live todo_write re-establishes the binding. Also drop the bulk-load restore that could never be read before a plan-mode transition cleared it, and pin the workflow gates and mode-entry clears with negative tests. * test(web-shell): pin older plan revision in ChatPane approval test (#8393) * test(cli): pin unbindable plan updates in approval revision test (#8393) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): clear Todo plan revision on history restore (#8393) restoreHistory was the one history-resetting path that kept activeTodoPlanRevision, so a restored snapshot could let a stale revision bind the next exit_plan_mode approval. Clear it like the sibling reset paths, pin the behavior with a test, and pin the live-load clear ordering after the replayed updates. * fix(cli): restore Todo stop guard clear on plan re-select (#8393) The previous-mode guard added for the revision binding also skipped the Todo Stop Guard trust clear on a redundant plan re-select; scope the guard to the revision reset so every transition into plan clears the stop guard as before. The replay-time revision clears now run in finally blocks so a transport failure part-way through a replay cannot leave a replayed binding on the live session, and the web-shell exit-plan approval rule is unified in one predicate. Revision tests assert through the observable qwenTodoApproval approval metadata instead of the private field. --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
ac67de2e46
|
fix(review): stop the reverse-audit loop while there is still time to report (#8468)
* fix(review): stop the reverse-audit loop while there is still time to report Measured on CI run #8368 (+1699 lines): the iterative reverse audit ran to its 5-round cap, each round a per-chunk fan-out whose findings then went back through verification, and the loop consumed 3.5 of the job's 4 budgeted hours. The outer GNU-timeout kill arrived while round 5's findings were still being verified. The review died holding every confirmed finding it had; nothing reached the pull request. The loop's rounds are driven by the orchestrator, but every round begins at the same place: agent-prompt building the round's prompts. So the builder becomes the loop's clock. When the environment carries a review deadline (QWEN_REVIEW_DEADLINE_EPOCH, exported per attempt by the review workflow) and the remaining time is inside the reserve kept for the last verification, compose-review and submission (default 60 minutes, QWEN_REVIEW_DEADLINE_RESERVE_SECONDS to override), a reverse-audit round is refused: a BUDGET line on stderr, exit code 4, no prompt built and no record written. The message carries the exact unreviewedDimensions entry to file, so the disclosure that caps the verdict is the CLI's text, and Step 6 proceeds with the findings already confirmed. Local runs have no deadline and are untouched. A malformed deadline fails open — the outer kill still bounds the run, and a broken variable must degrade to today's behaviour rather than wedge every budgeted review at round 1. The verifier is deliberately not gated: the reserve exists so it can run. * fixup: scale the deadline reserve to the externally-chosen budget The budget is not this workflow's to assume: it arrives from a repository variable, a workflow input, or a /review --timeout=N comment. A fixed 60-minute reserve would consume most of a 70-minute budget and refuse the audit loop outright on a 30-minute one. The workflow now passes a reserve of a quarter of the attempt, floored at 10 minutes and capped at 60; the CLI constant remains only the fallback for a caller that sets a deadline without a reserve. * review feedback: admit the round only if IT fits, and cap deterministically Three findings from review, all taken: 1. The gate budgeted for the tail but not for the round it admits — the terminal round is by construction the one that starts closest to the boundary, so the killed-mid-verification failure survived one round wide. The gate now requires remaining >= round + reserve, where the round's cost is the previous round's, measured admission-to-admission from a stamp the builder writes (one per round; a same-round rebuild is not a round), falling back to a 30-minute constant for round 1, which starts with the most headroom. 2. The refusal was deterministic; the disclosure that caps the verdict was prose the orchestrator had to carry. The builder now records a budget-stop marker beside the prompt records and compose-review synthesizes the unreviewedDimensions entry from it — deduped against a relayed copy — so a run that drops the sentence still cannot approve past a truncated audit. 3. Exit code 4 is documented in the command's describe. Also restores the Step 5 bullet the previous commit's edit displaced (new findings merge into the cumulative list before the next round). * review feedback: pin the budget gate's all-chunks refusal and ordering Cover the two behaviours the review noted were only asserted on the bare --findings form: an exhausted budget refuses the loop's real --all-chunks round before ANY of the per-chunk records is written, and a malformed call (--round 0) still gets its validation error first — exit 4 is for a well-formed round the budget refuses, never a replacement error. Also name what the code already does: reserve=0 is the deliberate escape hatch (the gate shrinks to the round estimate alone), and the workflow's 3600s cap mirrors DEFAULT_RESERVE_SECONDS. * docs(review): describe the soft-deadline env vars for time-budgeted runs The review noted the two new variables appeared in no user-facing doc; the reserve in particular is an operator-facing knob. State what each does, the fail-open posture, and how the refusal surfaces in the verdict. * fix(cli): align budget-stop disclosure with the gate's refusal (#8468) A round-1 budget refusal left no reverse-audit records, so the Step 4/5 floor reported the deliberate stop as a rogue/unlaunched audit with a rebuild FIX the same gate deterministically rejects; the refusal's own disclosure was swallowed by the caller-echo dedup. The floor now stands down when the budget-stop marker exists, and compose-review renders the disclosure structurally, bilingually, from the marker. Also: `--role reverse-audit` requires `--round <k>` (an unlabeled admission stamps an entry no estimate can attribute), the budget gate runs after the plan/findings reads (a broken plan or unreadable findings deserves its own error, and nothing is stamped ahead of a buildable call), and the gate's admission boundary, measured-cost behaviour, and the workflow env contract are pinned by tests. * review: a budget stop excuses only the round it refused The budget-stop suppression keyed on the marker's existence alone, so every reverse-audit gap shape went silent once any round was refused — including the shapes that describe rounds which RAN before the budget hit. A hand-written round-1 launch is exactly as undelivered when round 3 later hits the budget, and suppressing its disclosure let 'stopped before round 3' imply the rounds that did run were faithful. Exactly one shape is by design under a marker: not-built — the refusal writes no record, so an audit with no records IS the audit the gate stopped, and its FIX (rebuild the round) would be refused by the same gate. The suppression now names that shape and no other; a rewritten, unlaunched or brief-unread round keeps its disclosure and its repair. The new test pins the operative halves: the verdict stays capped, the marker's disclosure posts, and the operator channel carries the rewritten round's exact repair. (The posted body collapses same-subject disclosures — both say 'reverse audit' — so the author sees the stop; repairs are acted on from stderr, where the rewritten fix rides.) * fix(review): fence budget state per run, and let gate errors beat budget stops Address the round-2 review threads on the reverse-audit budget gate: - Fence budget-rounds.json and budget-stop.json by the plan's own mtime. Every run rewrites the plan at its Step 1 capture, so records older than the plan belong to a previous run of the same PR: a run killed before cleanup no longer prices the next run's rounds off stale stamps (an hours-old stamp read as an hours-long round refused round 1 of a fresh budget) and no longer caps a later run's verdict on a stop that did not happen in it (R2-1, R2-2). - Refuse a structurally unbuildable plan (no chunks[], duplicate or non-integer ids) with its own error ahead of the budget gate, so the same corruption gets the same diagnosis whatever the clock says, and no budget-stop marker is written over a corrupt plan (R2-5). - Stamp a round admitted only after its build succeeds: a build that throws leaves no stamp, so the next round's cost is never measured from a build that produced nothing and floored to 600s (R2-6). - Keep the budget entry's 'reverse audit' subject out of the caller-echo prefix filter: other reverse-audit scopes the orchestrator disclosed (a twice-whiffed chunk from the rounds that DID run) are no longer silently dropped in the marker's shadow; the marker's own relays stay deduped by the phrase splice (R2-7). - Render --round unbracketed in the reverse-audit rebuild fix — the CLI refuses a round-less reverse-audit call, so the paste-and-run repair must not present the flag as optional (R2-14). - Document the deliberate one-verification overlap between the measured round estimate and the tail reserve, at both definitions (R2-13). - Test hardening, each assertion mutation-probed to fail its named mutant: a reshaped relay only the marker-phrase splice dedups (R2-8); the stamp's round label and the verifier's no-stamp invariant (R2-9); whole-line, unit-arithmetic and reserve-cap pins on the CI wiring contract (R2-10); the first-wins stamp survivor (R2-11); the reserve=0 escape hatch (R2-12). --------- Co-authored-by: verify <verify@local> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
20b3087aca
|
feat(browser-ext): add alpha readiness diagnostics (#6739)
* feat(browser-ext): add alpha readiness diagnostics * test(browser-ext): automate readiness verification * fix(browser-ext): support current devtools adapter * test(browser-ext): verify restored page after reconnect * fix(browser-ext): harden release and acceptance checks * test(browser-ext): cover onboarding transitions * fix(browser-ext): harden alpha diagnostics * test(cli): sync serve capabilities baseline * feat(browser-ext): add alpha readiness diagnostics * test(browser-ext): automate readiness verification * fix(browser-ext): support current devtools adapter * test(browser-ext): verify restored page after reconnect * fix(browser-ext): harden release and acceptance checks * test(browser-ext): cover onboarding transitions * fix(browser-ext): finalize Chrome Web Store package * fix(browser-ext): harden CDP diagnostics per review feedback (#6739) * fix(browser-ext): harden CDP diagnostics per review feedback (#6739) Distinguish the ACP child's idle placeholder (initialized: false, discoveryState: 'not_started') from a genuinely empty server list so the panel no longer shows a false "adapter is not connected" warning before the first session or after the child is reaped. Compare the tunnel endpoint's host+port against the daemon baseUrl to detect cross-daemon shadowing (a chrome-devtools entry pointing at a different daemon's /cdp was previously reported as connected). Guard package-extension and symlink tests with skipIf(process.platform === 'win32') so the Windows merge-queue gate does not fail on missing zip.exe or privilege-dependent symlinkSync. Also: destructure QwenCapabilityStatus lazily inside probeState so a missing capability-status.js no longer throws before the welcome screen renders; add the missing license header to manifest-version.js; replace the leftover #welcome height:100vh with flex sizing; add cross-reference comments for the shared /cdp path pattern. Note: probeJson intentionally drops the .catch(() => ({})) fallback so a 200 with a non-JSON body reads as unreachable; this also makes /health stricter than before. * fix(browser-ext): resolve CDP diagnostics review findings (#6739) * fix(browser-ext): mirror nightly build number in manifest test oracle (#6739) * fix(browser-ext): address alpha diagnostics review feedback (#6739) - declare the semver dependency used by manifest-version.js so an isolated workspace install no longer relies on root hoisting - make artifact-scan skip the root CLI bundle metafile with a warning when it is absent (it only exists after `cross-env DEV=true npm run bundle`), keeping the extension metafile required, so package-level test:release no longer fails - throttle the side panel /workspace/mcp probe to every 5th tick and reuse the cached snapshot in between, avoiding a cross-process RPC on every 2s poll - document the per-session CDP event fan-out and pin single-path event counts; note that Target.getDevToolsTarget is deliberately unsupported - guard the nightly build-number git lookup and the zip end handler - disclose the daemon-to-model-provider page-content flow in PRIVACY.md - drop brittle source-substring panel tests and add coverage for a chrome-devtools server with no config args * fix(browser-ext): improve acceptance diagnostics and honest phase naming (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(cli): stabilize flaky orphan-session transport tests (#6739) Replace hardcoded setTimeout(40ms) + assertion with vi.waitFor() in the session/new and session/load orphan tests. The 40ms budget is too tight under CI parallelism, causing intermittent removeSession-not-called failures. vi.waitFor polls until the assertion holds (default 1s timeout), matching the pattern already used elsewhere in this file. * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(cli): restore PAGE_SESSION_ID forwarding for lazy-attach path (#6739) The autoAttachActive gate on PAGE_SESSION_ID command forwarding broke the cdp-ws lazy-attach path, which sends commands with PAGE_SESSION_ID without a Target.setAutoAttach handshake. Revert the forwarding gate to unconditional PAGE_SESSION_ID acceptance while keeping the gated Target.attachedToTarget emission (the Critical fix). * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): make survivor tests load-bearing with log assertions (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): address review feedback on diagnostics PR (#6739) * fix(browser-ext): resolve review findings on diagnostics tests (#6739) - reject preview-range QWEN_CHROME_EXTENSION_BUILD_NUMBER values at the env var boundary with a message naming the variable, value, and range - assert the package-extension symlink test observably ran main() instead of passing on equality alone when both runs fail identically - add CLI-level tests proving explicit positional roots are scanned and a clean scan exits 0, covering paths the symlink-only tests skip on Windows Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.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@service.alibaba.com> Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
e946fdbd3b
|
chore(release): v0.21.5 (#8505)
* chore(release): v0.21.5 * docs(changelog): sync for v0.21.5 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
7dfc554dff
|
feat(review): Add structured Web Shell review results (#8402)
* feat(review): add Web Shell review artifacts Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * test(web-shell): add code review artifact visual scenario (#8402) * fix(review): address Web Shell review artifact feedback (#8402) * save-artifact: document why paths resolve against the daemon workspace root (QWEN_CODE_PROJECT_DIR) instead of cwd, and cover the relative-path form the skill documents with a test where the two roots differ. * CLI/renderer contract: the renderer hand-duplicates the findings vocabulary and fails closed on unknown values, so name the renderer as a second consumer beside the CLI's lists and check in a contract fixture generated through the real pipeline (validateFindings -> buildReport -> save-artifact) that exercises every source, severity, confidence and outcome. Exporting the vocabulary through the SDK stays deferred: it is a public cross-package API change beyond this PR's seam. * resolve-anchors now validates `line` exactly like `findings` does (positive safe integer); the two validators in one pipeline no longer disagree. Note: an in-flight `.qwen/tmp` findings file carrying `line: 0` fails where it previously did not. * The renderer validates markdownReportPath (relative, no ".." segments, .md suffix) before it becomes a readWorkspaceFile call, resets the severity/confidence filters when switching artifacts, and surfaces heldByMeasurement so a nonzero Held count is attributable. * save-artifact refuses low effort structurally (choices and library guard) instead of by prose, stats the Markdown report before reading it so a directory reports "not a file", and the component no longer shadows the DOM `document` global. * The case-insensitive alias test now skips visibly on case-sensitive filesystems instead of passing vacuously. * Comment the kept `turnOutputs.review` key and document the JSON companion in the user docs. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(review): address second Web Shell review artifact feedback round (#8402) --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> |
||
|
|
c7a2a691ec
|
fix(review): stand a drifted launch whose payload provably arrived (#8466)
A model asked to copy the roster's twelve blocks normalized one word in
every block's tail ("you" -> "it"). Every launch failed the verbatim
containment check, check-coverage reported the whole roster undelivered,
and the run relaunched all twelve agents -- the most expensive repair in
the pipeline, spent redelivering text the agents had already acted on.
Measured on a live run: ~10M input tokens and 17 minutes of wall clock.
The verbatim check was written when the launch prompt carried the
payload. It no longer does: the brief on disk holds the method, the
severity bar and the project rules, and the transcript records whether
the agent opened it and whether it read the diff. When both facts are on
record, a drifted launch is a delivery that happened, not one that
failed.
check-coverage now reports such launches under driftedLaunches -- a
NOTE, not a failure: ok stays true, nothing enters the posted body, and
no relaunch is owed. The rescue is injective like the verbatim matching
(one transcript, one requirement) and requires the diff read for a role
whose brief reads the diff, so every true failure -- a drift with no
brief-open, a dropped read list, a hand-written prompt with no record --
stays exactly where it was. Step 4/5 delivery classification is
unchanged: a verify/reverse-audit launch carries the findings list in
the prompt itself, and drift tolerance there would excuse a dropped
payload.
Co-authored-by: verify <verify@local>
|
||
|
|
0cb109f513
|
fix(core): Avoid replaying unsafe MCP tool calls (#8387)
* fix(core): Avoid replaying unsafe MCP tool calls Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): Revalidate MCP replay after reconnect Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
d1648b3af9
|
feat(telemetry): Track tool execution outcomes (#8180)
* feat(telemetry): track tool execution outcomes Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(telemetry): address execution-status review feedback (#8180) - Update stale nonInteractiveToolExecutor expectations for executionStatus (red CI) - Scope the cancelled span-status short-circuit to tool_call events so other cancelled events carrying an error keep ERROR status - Record loop-detection skips as UNKNOWN, not EXECUTION_DENIED, keeping the denial metric accurate - Assert execution_status on the resolved-with-error PostToolBatch path - Raise tool-call observer-failure logging from warn to error - Clarify subagent-projection exclusion and JSONL compatibility in design doc * fix(core): address review findings 3-6 on tool execution status (#8180) - recordToolExecutionMetrics now merges common attributes (session.id opt-in) like every other counter in metrics.ts - Lift TOOL_FAILURE_KIND_ATTRIBUTE / TOOL_FAILURE_KIND_CANCELLED into telemetry/constants.ts so coreToolScheduler and session-tracing share one definition - Add debugLogger to runToolTelemetrySink catch (was silent) - Replace delete-based absence in withPostToolBatchStop with conditional spread * fix(core): address remaining review findings on tool execution status (#8180) - Pass the frozen executionStatus variable instead of the literal 'success' in the post-hook-stop error response, keeping the frozen value the single source of truth (finding 4) - Force-finalize the deferred PostToolBatch parent span in the abort drain, since that terminal path cancels the batch hook that otherwise owns the span; documents the invariant at the call site (finding 6) - Comment the loop-detection guard so the permission-cancellation exclusion from invalid-param loop detection is explicit (finding 9) - Rename the design doc to the dated docs/design convention and note the schedule()/handleConfirmationResponse() resolution contract change for embedders (finding 2, doc convention) * docs(core): note schedule() resolution contract in tool execution status design (#8180) Record the embedder-facing behavior change that schedule() and handleConfirmationResponse() resolve with a terminal error call rather than rejecting, so a failing tool no longer aborts its siblings. * fix(telemetry): address review feedback for tool execution status (#8180) - Document the new tool_call attributes (call_id, execution_status), the qwen-code.tool.execution.count metric, the tool.execution span attributes, and the tool.failure_kind=cancelled span field in telemetry.md. - Pass ToolErrorType explicitly at loop-detection skip sites instead of inferring it from the skip message string, so copy edits cannot silently reclassify loop skips as approval denials. - Simplify withPostToolBatchStop response construction (drop the destructure-and-reattach used to preserve a missing execution status). - Add a debug breadcrumb when a PostToolBatch stop has no span to attach to, and a one-time warning when PostToolBatch hook detection fails open. - Drop the try/catch wrapping the pure isTelemetrySdkInitialized getter. - Clarify the design doc invalid-combination wording and note that the execution-failure SLI cannot be attributed to a specific tool. - Add a regression test pinning that schedule() resolves (not rejects) when a tool execution throws. * fix(telemetry): address round-7 review feedback for tool execution status (#8180) * fix(telemetry): restore type-safety fallback for executionErrorType (#8180) * fix(telemetry): align tool execution failure outcomes Keep Core and ACP cancellation arbitration consistent, preserve structured post-processing errors, and restore QwenLogger MCP metadata privacy. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(core): address review suggestions for tool execution status (#8180) * test(core,cli): strengthen test-efficacy for tool execution status (#8180) * fix(core): address review suggestions for tool execution status (#8180) - Improve post-processing cancellation message to indicate the tool had already completed, preventing silent model redo of completed work - Remove dead !isExecutionTimeout conjunct in Session.ts PostToolUse cancellation check (unreachable: timeout always sets toolResult.error) - Replace construct-then-delete with destructuring in withPostToolBatchStop - Move all failure-kind constants to telemetry/constants.ts so the full documented vocabulary lives in one place - Re-export StructuredToolError from tool-error.ts instead of importing from the unrelated priorReadEnforcement module - Add JSDoc to normalizeToolCallEvent documenting key-absent semantics - Add ordering-safety comment to createParentAbortRace microtask guarantee - Document endToolExecutionSpan not_started guard as defence-in-depth - Document PostToolBatch span leak window in finalizeToolSpan - Add design doc note about hand-placed cancellation check invariant - Add test for unknown execution_status normalization path - Revert unrelated generate-notices.js formatting change * fix(core): address review feedback for tool execution status (#8180) - Gate cancel message on executionThrew so the model sees 'User cancelled tool execution.' when execute() rejected under abort, reserving 'already completed' wording for post-processing cancels - Move StructuredToolError into tool-error.ts to break the tool-error ↔ priorReadEnforcement module cycle - Revert unrelated Prettier reformat in generate-notices.js * test(core): pin both tool cancellation notices; extract them as constants afd349ca gated the cancel message on executionThrew but left the two wordings as bare literals at four sites and added no test. That is the exact shape the bug had: it was introduced by editing one literal and missing the others. Extract TOOL_CANCELLED_{BEFORE,AFTER}_COMPLETION_MESSAGE so the four sites cannot drift, and add regression tests for both paths — a tool interrupted mid-flight (execute() rejected under abort) must report "User cancelled tool execution.", while a cancel after execute() returned must report that the output was discarded. The mid-flight test fails against the pre-afd349ca behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(core): keep MCP reconnect for a timeout on a dead transport Classifying every `-32001` as EXECUTION_TIMEOUT skips handleReconnectOnError, which previously recovered one real case: the transport dies mid-request, the SDK request times out because no response will ever arrive, and the server is already recorded DISCONNECTED. That reconnected and retried; now it hard-fails and the user has to retry by hand. Divert back to the reconnect path only on positive evidence the transport is dead. Note that getMCPServerStatus() reports DISCONNECTED for servers it has never seen, so the guard checks for a *recorded* DISCONNECTED — the naive comparison misroutes every timeout from a server whose status was never registered, which broke four existing timeout tests when tried. A timeout on a healthy server is still EXECUTION_TIMEOUT: retrying it after a reconnect would just double the wait. The client-side idle timeout keeps classifying unconditionally; it is our own timer, not a transport signal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(core): address blocking review feedback for tool execution status (#8180) Two blocking items from the maintainer review: 1. Post-processing cancellations dropped persistedOutputFiles (and visionBridgeNotice) along with the model-visible output, orphaning files the tool had already spilled to disk. createCancelledResponse now carries both, and every cancelAfterPostProcessing site passes what it has; the settle-then-abort and hook-stop paths do the same. 2. A -32001 that lands while the parent signal is aborted is the SDK's abort rejection or a timeout that raced with a cancel; classifying it EXECUTION_TIMEOUT would count user cancels against the timeout SLI. isExecutionTimeoutFailure now defers to the abort in both catch blocks, regardless of which side settled the race first. The two tests that pinned the opposite timeout-wins ordering are updated to the abort-wins semantics the review asked for. Co-Authored-By: Qwen Code <noreply@alibaba-inc.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com> Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: qwen-code-bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: qwen-code-ci-bot <253268222+qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Qwen Code <noreply@alibaba-inc.com> |
||
|
|
0cbb6a1f1c
|
chore(release): v0.21.4 (#8424)
* chore(release): v0.21.4 * docs(changelog): sync for v0.21.4 --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> |
||
|
|
2ae8cd9619
|
feat(memory): configure background agent turn limits (#8171)
* feat(memory): configure background agent turn limits Signed-off-by: ahmadalguydi <ahmadalgaidy@hotmail.com> * fix(memory): honor configured skill review turn limits Signed-off-by: ahmadalguydi <ahmadalgaidy@hotmail.com> * test(memory): cover planner turn limit sentinels * fix(memory): apply turn limits to all agents * test(memory): cover remaining turn limit feedback --------- Signed-off-by: ahmadalguydi <ahmadalgaidy@hotmail.com> |
||
|
|
9342788720
|
fix(review): deprioritize Maven generated test sources (#8405)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
4fc856801f
|
feat(cli): adopt Goal v3 in non-interactive mode (#8324)
* feat(cli): adopt Goal v3 in non-interactive mode * test(cli): cover ACP goal control rejection * test(cli): cover queued Goal user turns * fix(cli): fail closed before session turn exit --------- Co-authored-by: qwen-code-dev-bot <269191875+qwen-code-dev-bot@users.noreply.github.com> |
||
|
|
fa938bdfac
|
feat(web-shell): gate session workflow behind experimental setting (#8391) | ||
|
|
92c2889cd5
|
feat(cli): add a Java/JVM performance path rule to /review (#8379)
* feat(cli): add a Java/JVM performance path rule to /review
The review's dimensions are domain-blind, and a Java diff's most
expensive regressions are decided by the JVM, not by anything visible
in the source: HotSpot chooses what to inline and what to compile by
the callee's bytecode size (MaxTrivialSize 6 / MaxInlineSize 35 /
FreqInlineSize 325 / HugeMethodLimit 8000), and a one-line change can
flip it on a hot path.
Like the GitHub Actions rule before it, the checklist attaches to
*.java paths and reaches every code-reviewing agent whose territory
contains one, scoped so non-Java diffs pay nothing. It carries:
- the correctness traps dressed as perf/concurrency code (shared
SimpleDateFormat, two-call ConcurrentHashMap compounds, DCL without
volatile) at Critical;
- the JVM-cost defects provable from source (per-call regex compiles,
loop string +=, hot-path boxing, capturing lambdas in loops,
unconditional log-message building, unpresized collections, legacy
synchronized types, exceptions as control flow, per-call
reflection) at Suggestion;
- the JIT inlining thresholds with a two-tier verification discipline:
measure with javap against base and head (never estimate bytecode
from source), or run -XX:+PrintInlining / JMH when the code is
runnable; unmeasured inlining claims are reported as mechanism at
low confidence.
Dogfooded against alibaba/fastjson2#3992 (BigDecimal parsing perf):
the performance agent applied the threshold reasoning correctly —
readBigDecimal was already far above FreqInlineSize before the diff
and the change shrinks it, so no crossing was possible and no
measurement was owed, stated with exactly that justification.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* feat(cli): name hot/cold splitting as the fix for a grown hot method
An A/B experiment measured what the checklist adds on top of the
model. Four crafted Java diffs (a hot method grown past FreqInlineSize,
a map-backed cache, a per-element append loop, a precompiled regex
parser) were each reviewed by a blind Agent-4 equivalent with and
without the Java path rule, on qwen3.8-max-preview.
Without the rule, the performance agent never once considered
inlining across all four diffs, and filed a high-confidence finding
that a constant long division costs 20-90 cycles per iteration —
bytecode-true, but C2 strength-reduces constant division to a
multiply-by-magic-number, so the cost does not survive the JIT. With
the rule, it measured every diff with javap (base 80 bytes, head 338,
crossing FreqInlineSize at 325), reported the crossing at low
confidence with the tier-2 check named, dismissed the division with
the correct mechanism, and proposed the fix as a hot/cold split with
the exact bytecode range to extract.
The fix shape is the part the model does not supply on its own, so
the checklist now names it: move cold paths into a private helper,
never @ForceInline (which bloats every caller), and state the
extraction as a bytecode range and a resulting size. The same
experiment cut three candidate additions — dense-key cache container
choice, per-element-to-bulk loops, and regex-for-fixed-formats —
because the control runs reached the same findings without them.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): address review on the Java/JVM path rule
Seven findings from the PR review, all verified before fixing:
- The static measurement tier prescribed 'compile the base revision the
same way', which reads as git checkout/stash in the one worktree nine
agents share concurrently — or in the user's own checkout in local
mode, where reviewsCode agents get no 'do not build the main checkout'
guard (that guard is role-7-only). Rewritten as a non-mutating
procedure: extract the base side with git show into a scratch dir,
javac -d there, never checkout/stash/build in place.
- A full mvn/gradle build runs the branch's contributor-controlled build
logic; the checklist now says to prefer javac on the extracted file
and treat any build it does run as untrusted code (Agent 7's brief
already carried this caveat; the rule extended the capability to nine
agents without it).
- InlineSmallCode cited as ~1000 (the pre-JDK-11 value); measured 2500
on a live JVM. HugeMethodLimit is a develop flag gated by the product
DontCompileHugeMethods, and the boundary is > 8000, not >= 8000.
- 'Megamorphic -> no inlining at any size' overstated C2: a dominant
receiver (TypeProfileMajorReceiverPercent, 90%) is still inlined
behind a guard with an uncommon trap.
- pathRulesFor listed every triggering path in the heading of every
agent's brief; a 200-file Java PR put ~11 KB of paths there. Capped at
ten plus a count, for both rules.
- The flat 'performance findings are Suggestions' carried no escape
hatch; added the one the workflow rule already needed — unbounded cost
on attacker-reachable input is a DoS hole, graded Critical.
- Nits: the split fast-path excludes regex metacharacters (split(".")
does not take it); test and generated sources are out of scope for the
hot-path items; the Java match rows fold into the shared governed-table
test so both rules assert through PATH_RULES.matches.
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
* fix(cli): second-round review on the Java/JVM path rule
Six findings plus nits and three inline coverage probes, all verified:
- The 'safe' javac tier still executed contributor code: annotation
processors on the classpath run at compile time. Add -proc:none
(not optional), and make mvn/gradle a prohibition rather than a
discouraged preference — on a stranger's PR branch the build logic is
the attack surface.
- /tmp/<scratch> was a placeholder agents converge on; two compiling
different revisions of one class into the same dir measure each
other's bytecode. Prescribe SCRATCH=$(mktemp -d), with a %TEMP% note
for Windows.
- 'Extract and javac' fails on any class with imports. Name the
classpath path (-sourcepath at the module root, an existing
target/classes, or mvn dependency:build-classpath which resolves
without building) and a graceful fall-back to the mechanism tier
instead of escalating to a project build.
- @ForceInline was ruled out for bloating callers — true but secondary,
and the annotation is JDK-internal, not general. Lead with the
anti-pattern that actually bites app code (reaching for -XX:FreqInlineSize
/ -XX:CompileCommand=inline, runtime knobs a PR cannot ship) and note
@ForceInline only as unavailable.
- describePaths capped in diff order, so a test-heavy PR could name ten
test files the rule scopes out and no production path. Stable-partition
production first.
- Header: a rule earns its place by naming an invisible defect AND pays
a per-agent token cost; say so, before rule #3 arrives.
- Nits: HashMap.newHashMap(n) (JDK 19+) for the presize arithmetic; the
split fast path also covers the escaped two-char form.
- Coverage probes (inline): the correctness-traps block and the nine
Suggestion patterns had zero test coverage — deletion left all tests
green. Pin the load-bearing strings of both, plus the new tier flags
and the production-first ordering.
* fix(cli): third-round review on the Java/JVM path rule
Drop the mvn dependency:build-classpath recommendation — Maven extensions
execute during any invocation, a strictly larger execution surface than
the annotation processors -proc:none exists to close. Fall through to the
mechanism tier when no pre-built classpath exists.
Add --release <N> at the project's target level: the same source compiles
to different bytecode at different levels (61 vs 16 bytes for a five-+
concatenation), so measuring without it produces a threshold verdict on
bytecode the shipped artifact does not contain.
Name -proc:none as a fidelity hazard: on a Lombok/Dagger project the
compiled class is missing generated members, so the static tier is void.
Add clauses for new files (no base side to compare), the base-side
target/classes caveat, and the Windows uniqueness primitive.
Fix the vacuous ordering assertion (indexOf returns -1 outside the cap,
and -1 < n passes). Add a GHA cap test and a --release/new-file test.
* fix(cli): fourth-round review on the Java/JVM path rule
* fix(cli): fifth-round review on the Java/JVM path rule
* fix(cli): sixth-round review on the Java/JVM path rule
* fix(cli): seventh-round review on the Java/JVM path rule
---------
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com>
|
||
|
|
e1e5b42ce1
|
fix(review): hold a Critical the base tree already fails (#8380)
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
* fix(review): hold a Critical the base tree already fails
`test-delta` reruns the PR side's failed test commands on the merge base and
splits the failures into the PR's own (`netNew`) and pre-existing (`shared`).
Nothing then read that split back. A Critical asserting "this PR breaks test X"
could name a file the same run had just measured as failing without the PR, and
the two artifacts never met.
Measured on #8368: `AuthDialog.test.tsx` came back `shared` in two independent
runs, and the merge base
|
||
|
|
783787c8ad
|
fix(review): read why the collocated test was not green, do not assert it (#8374)
* fix(review): read why the collocated test was not green, do not assert it A mutant or hunk whose own collocated test was not green in the unmutated baseline is held `inconclusive` -- correctly, since the other probes passing shows only that THEY do not cover it. But both guards then named a cause they had not measured: "likely a compile or import error in the probe tree". There are two ways a probe is not green and they are different failures with different fixes. Measured on PR #8368: `AuthDialog.test.tsx` compiled fine, collected 26 tests and failed exactly one, and all three mutants in its source were held with the import-error wording -- sending a reader after a problem that was never there. The baseline had already classified it `gated` (a real assertion failure) rather than `inconclusive` (collected nothing); the guards just did not look. Both now build their detail from one exported function that reads the verdict off the baseline, so the two sentences cannot drift apart again and what the tests pin is the string the report carries. A probe with no baseline entry takes the collected-nothing wording: absent is an evidentiary hole, never the claim that its tests failed. * fix(review): carry why a probe was inconclusive, and decide the hold once Two findings from review, both landing on this PR's own thesis. The else branch told every non-`gated` probe the same story -- "collected no tests there (a compile or import error…)" -- but `classifyProbeRun` reaches `inconclusive` three different ways, and one of them is the runner producing no parseable output at all. Nothing is known about collection there, so naming a compile error is the same invented cause this PR was opened to remove, one layer down: a reader sent hunting for an import problem while the runner itself is what fell over. The classifier now tags each branch with a machine-readable `reason` -- it already had the three cases -- and the explanation reads it. An entry that names no reason gets the disjunction; a probe with no entry at all is reported as not measured, which is a different thing from measured-as-empty. The rule itself was stated twice, once per loop, and the duplication had drifted twice already: the hunk loop had the guard first and eight mutant survivors shipped through the gap before it was copied across, then the shared wording was corrected in one place and hand-copied to the other. Both loops now call one exported decision, `heldForRedCollocatedTest`, which is where the tests point. Three mutations, each reddening only its own case: collapsing `no-output` into the compile-error wording, treating an absent probe as an inconclusive entry, and a guard that never holds. * fix(review): make an untagged inconclusive a compile error The tags this PR turns on were the only part of it nothing tested. `reason` was optional and the lookup absorbed a missing one, so deleting a tag left all 116 tests green while every hold of that kind silently degraded to a vague catch-all -- the same defect this PR exists to remove, one layer down. `ProbeResult` is now a discriminated union: `reason` is mandatory on the `inconclusive` arm and absent from the others, so an untagged branch does not compile. Deleting a tag is now caught twice, by tsc and by the new assertions on `classifyProbeRun` itself, which no fixture stood in for before. `no-tests` was also two different observations under one name. `!result` means the run answered and this file was not in the answer -- which a path that failed to match produces as readily as a compile error, and the boundary and case rules right above it are why that is not hypothetical -- so it is now `not-in-results` and says both possibilities instead of picking one. Also from the review: the `collocatedNotGreenDetail` doc block had been left above `heldForRedCollocatedTest`, describing neither, and its last paragraph stated the opposite of what the code returns for an unreported probe. It moves onto the function it documents and now matches it, including that the case is a default rather than one the pipeline can reach. `collocatedProbe` takes a readonly array, dropping a per-candidate copy made only to satisfy the type. * fix(review): let the union reach the array the report serialises The invariant was argued for and then not checked where it counts. `probed` serialises `results`, and `results` was declared as a structural echo of `ProbeResult` rather than `ProbeResult` itself, so two catch paths pushed an untagged `inconclusive` and compiled -- and a third assigned `verdict` in place, which the union cannot see at all. The artifact therefore carried `reason` on classifier-produced entries and not on the others: two conventions for one verdict, described in the PR as one. `results` is now `ProbeResult[]`. That turned all three into compile errors and they needed reasons of their own, neither of which is `no-output`: `not-run` for a probe no suite was attempted for, and `control-failed` for one that read green in a run the positive control had already proved could not go red. The in-place re-class is a replacement now, since assigning `verdict` alone leaves the entry untagged. `ProbeOutcome` was re-opening the optionality one line after the union closed it -- a plain `Omit` collapses the arms -- which let an `inert` entry reach the explanation helper and come back described as "did not come back green", the opposite of what `inert` means. A distributive omit keeps the discrimination, so the helper cannot read `reason` without narrowing first, the `inert` arm has to be written out, and the `unspecified` phrase stops existing rather than being documented as unreachable. Also: `ProbeReason`'s doc described three members of what is now a five-member union and gave `no-tests` the definition of `not-in-results` -- the branch this PR split away from it. And `not-in-results` was the one reason whose report sentence nothing asserted. * fix(review): a probe that was killed is not a probe that never ran `not-run` was applied to a catch covering three different failures. The checkout and the tree removal fail before anything runs; the runner throws when it was KILLED, and the per-run timeout fires SIGTERM at a suite that may have executed most of its tests. Tagging that "no probe suite ran" is a cause nothing measured -- the move this file exists to stop, one layer up, in the machine-readable field the description argues a consumer can act on. Split into `runner-died`, derived from the runner's own message prefixes by an exported `probeFailureReason` so both directions are pinned rather than assumed. Three reasons are set only on the run-level results array and can never reach `collocatedNotGreenDetail`, which is passed the baseline. Rendered there, `control-failed` produced "did not run green in the unmutated baseline -- it read green there", a sentence contradicting its own frame. They now report that the baseline did not classify the probe, the same answer the `inert` arm gives, and the parameter is named `baselinePerFile` for the contract it has. The `it.each` cast named three of the six reasons the table feeds, so it compiled regardless and a typo would have surfaced as a confusing `toContain` diff instead of a type error. It is `ProbeReason` now. And the integration hold assertions matched what the old flat wording also satisfied. They now pin the clause that regressed, so the chain the bug shipped on -- baseline classification, reason tag, sentence -- is covered end to end. * fix(review): read the runner's failure from the result, not from its prose `probeFailureReason` matched `/^runner (killed|spawn failed)/` against the thrown message, and neither real failure produces that text. Measured: `spawnSync` reports a timeout as `error` (`spawnSync … ETIMEDOUT`, with `signal` also set) and a missing binary as `spawnSync … ENOENT`, and `runProbeSuite` throws `r.error` before it ever composes a "runner killed by" sentence. So `runner-died` was never produced -- the tag added last round to stop a cause being invented was itself inoperative -- and the test that covered it asserted `runner spawn failed: ENOENT`, a string that exists nowhere but in that test. The reason is now read off the spawn result's structure by an exported `runnerFailureReason`, and carried on a `ProbeRunFailure` rather than left to be parsed back out. Its cases are driven through real `spawnSync` calls -- a process killed at a 300ms deadline and a binary that does not exist -- so the fixture is measured rather than written. `not-run` widens to "no suite ran: the tree could not be prepared, or the runner could not be started", which is what a spawn that fails actually is. Also from this round: the `ProbeReason` doc said "Five different things" over seven bullets, and `heldForRedCollocatedTest` still called its parameter `perFile` after the helper it forwards to was renamed `baselinePerFile`. * docs(review): make the reason phrasings match where each one now routes `runner-died`'s phrase still offered "or a spawn that failed" after a failed spawn was reclassified to `not-run` -- naming a case that no longer arrives there, which is the mismatch this change exists to remove. `not-run`'s comment gained the same case it acquired in the type. And the `results` declaration said "the two catch paths below" when the control-failed re-class is a third site the union now constrains. |
||
|
|
186812694c
|
feat(review): publish evidence images to a user-designated assets repo (#8351)
* feat(review): publish-assets — evidence images for PR review comments
GitHub's API cannot attach images to review comments (the web UI's
drag-and-drop upload has no API equivalent), so a review whose evidence is a
screenshot — a TUI rendering, a before/after comparison — had no way to show
it. New `qwen review publish-assets` hosts evidence images in a
user-designated repository and hands back URLs a comment can embed.
Grew from the maintainer's manual workflow (screenshots pushed to
`pr-assets/<PR>-verify` branches over HTTPS), and inherits the shape of the
skill's only other public write (`submit`) deliberately:
- Designated destination: writes only to QWEN_REVIEW_ASSETS_REPO, an
owner/repo the user set by hand — the reviewed repo for maintainers, a fork
or scratch repo otherwise (fork-vs-in-repo becomes a configuration
difference, not two code paths). A separate variable from
QWEN_REVIEW_SCRATCH_REPO on purpose: that contract forbids PR-derived
content, and evidence screenshots are exactly that. Unset → exit 3.
- Authorised run: the same args-file re-parse and target binding as submit,
now extracted to a shared lib/authorization.ts so the two gates cannot
drift (the target-binding lesson lives in one place). Since an effective
--comment forces high effort, low/medium runs can never publish.
- Images only, capped, all-or-nothing: extension allowlist (SVG excluded — a
script container), per-file and per-batch size caps, one refused file
refuses the batch before anything is pushed.
- Immutable references: files land on pr-assets/<pr>-review via the Contents
API (HTTPS via gh; no clone, no SSH), content-hash-named so re-runs are
idempotent, and every URL is pinned to the commit — a posted comment's
evidence cannot be changed from under it. The web-host /raw/ URL form works
unchanged on GitHub Enterprise.
- Auditable: a manifest names every file pushed and the landing commit,
swept by cleanup with the other review artifacts.
The findings artifact gains per-finding `assetFiles` (local evidence paths)
and `assets` (published URLs); `publish-assets --findings/--findings-out`
publishes everything referenced and weaves the URLs back in, so the comment
builder reads the artifact rather than hand-carrying URLs.
What the command cannot check is stated in SKILL.md instead: image content.
Publish only evidence the review itself produced — never a capture of the
user's own terminal, which can hold an env dump in the scrollback.
Tests: 45 files / 1394 assertions — new suites for the assets naming and
validation rules and the command's gates (refusal without designation,
refusal without authorisation, target binding, branch creation, idempotent
re-run, batch refusal, findings weaving); submit's 42 pass unchanged on the
extracted gate.
* fix(review): publish-assets round-1 self-review — six findings
Round-1 review of this branch, walked with the angles the author-side pass
does not cover:
- submit.ts kept its parseReviewArgs import after the authorization
extraction; vitest does not typecheck, `tsc --build` does, and CI's build
leg failed on TS6133. (The whole first CI round's failures cascade from
this one break.)
- ensureBranch %2F-encoded the slashed ref path; GitHub's documented form is
literal slashes and %2F routes inconsistently across endpoints — a 404
here reads as "branch missing" and turns every re-run into a 422 on the
create. Ref paths are now literal (the branch name is built from a
validated integer, so interpolation is safe); the contents `?ref=` query
VALUE keeps its encoding, which is the correct position for it.
- The authorization gate bound URL-shaped `--comment` arguments against the
ASSETS repo, refusing legitimately authorised runs whenever the assets repo
is a fork rather than the reviewed repo. The shared gate's repo binding is
now optional — submit still always binds it; publish-assets binds the PR
number (and host) alone, with a new optional --reviewed-repo to restore
the stronger binding when the orchestrator knows the reviewed repo.
- URLs were pinned to the last PUT response's commit.sha; on an
identical-content update that field's shape is GitHub's to decide, not
ours to assume. The head is now read from the branch ref after the
uploads — one extra call for independence from the response shape.
- putContent's catch-all retried EVERY failure through the exists path,
answering a 401 with a confusing secondary error from the sha lookup; the
retry now fires only on the 422/needs-sha shape and rethrows the rest.
- --findings without --findings-out silently skipped the URL weaving; it
now warns, and --findings-out implies --findings.
New tests: literal-ref assertion, non-exists rethrow, URL-shaped
authorisation without assets-repo binding, --reviewed-repo mismatch refusal.
45 files / 1399 assertions green; `tsc --build` clean.
* fix(review): publish-assets round-2 — empty-findings no-op, reviewed-repo hint
Round-2 findings on this branch:
- A findings artifact carrying no assetFiles is the ORDINARY case for most
reviews, but publish-assets answered it with exit 3 — a refusal an
orchestrator calling the command unconditionally on every posting run
would read as a failure to repair. It is now a no-op (exit 0,
{published:false, count:0}); a bare --files with nothing named keeps the
exit-3 refusal, because there the emptiness IS the caller error.
- SKILL.md's example now names --reviewed-repo for URL-target reviews, so
the stronger authorisation binding is used where the orchestrator knows
the reviewed repo.
44 files / 1387 assertions green; tsc --build clean.
* test(review): fix invalid two-argument expect in assets.test.ts
Round-3 sweep: vitest's expect takes one argument — the message-style second
argument was a lint error and a weak assertion both. The offending value now
rides inside the asserted object, so a regression names which shape slipped
through instead of reporting 'expected true'.
* test(review): pin the findings schema's evidence-asset validation directly
Round-4 sweep: assetFiles/assets were exercised only through publish-assets'
weaving test — the schema's own rejection paths (non-array, empty-string
entry, empty-array drop) had no direct case, so a validation regression
would have surfaced as a confusing weaving failure two layers up.
* fix(review): address all six findings from the automatic review (R1-1..R1-6)
The /review pipeline's own round-1 findings on this PR, each confirmed and
fixed:
- R1-1 (the real catch): the host-binding check sat nested inside the
`req.repo !== undefined` guard, so a caller omitting --reviewed-repo also
silently skipped the HOST binding — contradicting the documented "binds
the PR number (and host) alone". The host check now stands on its own;
a new test pins an Enterprise-host mismatch refusal with the repo binding
absent.
- R1-2: --pr accepted whatever yargs `type:'number'` passed through (NaN,
0, 3.5), and --user-authorized bypasses the gate that would have
re-parsed the target — `pr-assets/NaN-review` was reachable. A Gate-0
positive-integer check now refuses first, matching submit's sibling
discipline.
- R1-3: the suite drove the skillArgs seam without clearing
QWEN_CODE_SESSION_ID, so running it inside an active Qwen Code session
spuriously failed eight tests. beforeEach now saves/clears the variable
and afterEach restores it.
- R1-4: the 40MB aggregate cap was enforced inline and untested (a mutation
deleting it stayed green). The per-file rules and the total cap now live
in one pure ruling, validateAssetBatch, unit-tested with five 9MB sizes
and no fixtures.
- R1-5: the asset_files snake_case alias was the one untested member of an
otherwise-tested alias family; pinned.
- R1-6: the setGhHost wiring had no command-level assertion; a GHE test now
pins both the call and the host-carrying manifest URLs.
44 files / 1397 assertions green; tsc --build and eslint clean.
* fix(review): address all ten round-2 findings from the automatic review
Round-2 of the /review pipeline on this PR: 2 Critical, 8 Suggestions,
every one confirmed against the code and fixed.
Criticals:
- The round-2 test block added for the empty-findings no-op omitted the
QWEN_CODE_SESSION_ID save/delete/restore its two sibling blocks perform,
so the suite spuriously failed inside an active Qwen Code session — the
exact dogfooding environment this repo reviews from.
- The gh routing and the returned URLs read the host from two different
sources: with --host absent, gh children inherit an operator-exported
GH_HOST (routing at Enterprise) while rawAssetUrl defaulted to
github.com — every returned URL a 404. One effectiveHost (flag, then
GH_HOST env) now feeds both.
Suggestions:
- putContent's retry discriminator matched a bare `422` anywhere in
err.message — which execFileSync fills with the full command line,
including the PR-numbered remote path: evidence for PR #4220 would read
a 401 as "already exists". Anchored to `HTTP 422`.
- ensureBranch's bare catch read every ref-lookup failure (401, 403
rate-limit) as "branch missing"; only HTTP 404 takes the create path
now, and an empty assets repo — whose default_branch resolves while its
head ref 404s — is named as the condition it is, with the fix stated.
- Validation refusals threw (yargs exit 1, stack trace, empty stdout)
while every other gate in the command answers exit 3 +
{"published": false}; unreadable files and batch refusals now speak the
same refusal language.
- The command's idempotent writes (content-hashed PUTs, a ref create
whose duplicate is tolerated) now go through a new ghWithInputRetried —
sharing gh()'s transient-error retry — and ghWithInput's no-retry
docstring names the two-caller split instead of claiming a sole caller.
- parseAssetsRepo admitted dot-segment repos (`owner/..`) its docstring
claimed were path-safe; segments now exclude `.`/`..`, mirroring
submit's isRepo.
- stringArray accepted whitespace-only evidence paths; trim(), matching
the sibling asString.
- The GHE test asserted setGhHost was called but not WHEN; it now asserts
the call precedes the first API invocation.
44 files / 1403 assertions green; tsc --build and eslint clean.
* refactor(review): one refusal helper for every publish-assets gate
Round-2 of this branch's fresh review: the refuse() helper existed below
seven inline copies of the identical three-line refusal — the drift shape
where one site eventually forgets the exit code. Hoisted to the top of
runPublishAssets and used by every gate; message content unchanged where
tests pin it. 26/26; tsc clean.
* fix(review): address the round-3 review — bidirectional host binding and 14 more
The automatic review's third round on this PR: 1 Critical + 14
Suggestions, each verified and addressed.
The Critical (host binding, both halves):
- The gate's `req.host &&` guard bound the host in one direction only —
an Enterprise-URL authorisation admitted a host-less write routed at
github.com (or wherever GH_HOST pointed). The gate now compares the
authorised host against the write's EFFECTIVE host, defaulting an
absent req.host to github.com: a host is a host, not an exemption.
- Both callers fed the gate the flag rather than the route: publish-assets
computed effectiveHost (--host ?? GH_HOST) AFTER the gate and bound
args.host; submit bound args.host while its gh child inherited GH_HOST.
publish-assets now resolves effectiveHost before Gate 2 and binds it;
submit binds the same resolution.
The rest:
- pr-assets/<N>-review registered in the asset-branch cleanup workflow,
per its own every-producer-must-be-added-here rule — a branch nothing
deletes is permanent.
- ghWithInputRetried had been inserted between ghWithInput and its JSDoc,
leaving the does-NOT-retry comment attached to the function that DOES
retry; each function now carries its own doc.
- putContent's retry-path contents-GET is wrapped: when the 422 was not
the sha-missing shape and the path does not exist, the GET's 404 no
longer replaces the PUT error the user needs.
- stringArray treats null as absent like every sibling parser, so an
artifact rendering "no assets" as null canonicalizes instead of
crashing.
- Test isolation, all four describe blocks: GH_HOST save/delete/restore,
setGhHostMock.mockReset (a sibling's persistent throwing implementation
survives mockClear — the malformed-host test also switched to
mockImplementationOnce), and full mock resets in the blocks that lacked
them.
- The two regression-pin tests the review measured vacuous now
discriminate: each fails only the one call under test and asserts the
pipeline stopped THERE (no contents PUT after a bad create; exactly one
gh call after a 403 lookup).
- New positive pins: a double-fired create ("Reference already exists")
succeeds; the canonical report shape this command's own --findings-out
writes round-trips; an Enterprise-URL authorisation refuses a host-less
write while a github.com-URL one passes it.
Not changed: the finding that reverting the Finding-interface hunk leaves
tests green — the fields are type-level and their removal fails
tsc --build (the CI leg that caught this branch's own TS6133); a runtime
pin would duplicate what the round-trip tests already exercise.
47 files / 1495 assertions green; tsc --build 0 errors; actionlint clean
on the cleanup workflow.
* fix(review): address the round-4 review — empty-GH_HOST passthrough and four test pins
Round 4 came back COMMENTED (down from CHANGES_REQUESTED), 5 Suggestions,
0 Criticals — all five confirmed and fixed:
- An exported-but-empty GH_HOST ("" from an unset workflow var) survives
`??`, being non-nullish: effectiveHost became "" and the gate compared
the authorised host against "", refusing a legitimate github.com write.
Both call sites now collapse an empty trim to undefined (`|| undefined`,
parenthesized).
- The gate's URL-shaped repo/host binding was exercised only via
publish-assets' suite; submit.test.ts now pins both directions of the
host binding and the repo binding at its own call site.
- ghWithInputRetried had no retry-contract test; gh.test.ts adds the
symmetric block to ghWithInput's does-NOT-retry pin (transient 500
retried once then succeeds; non-transient 401 single call).
- The publish-assets mock aliased ghWithInput and ghWithInputRetried to
one mock, hiding which variant a write used; they are two mocks now,
and the happy path asserts the non-retrying variant is never touched.
- The Prepared interface's dead `name` field is gone.
46 files / 1476 assertions green; tsc --build and eslint clean.
|
||
|
|
3d1eba935f
|
fix(cli): re-stamp QWEN_CODE_CLI for the review run child (#8378)
* fix(cli): re-stamp QWEN_CODE_CLI for the review run child review run spawned its child CLI with the inherited environment, and cli.ts stamps QWEN_CODE_CLI first-writer-wins — so a review launched from inside a parent Qwen session ran the parent's installed build for every skill subcommand. Measured while dogfooding: a working-tree review run issued from a 0.21.3 session had its whole prompt roster built by 0.21.3, silently reviewing with a different version of the skill than the one running it. Compare the inherited entry against argv[1] by resolved path: keep it when it IS this build (npm bin shim, cli-entry.js, desktop bundle — the outer-launcher case first-writer-wins exists for), blank it otherwise. Empty counts as unset in stampCliEntryEnv, so the child re-stamps from its own modules. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): compare package roots, not exact files, in childEnv QWEN_CODE_CLI check (#8378) cli-entry.js stamps QWEN_CODE_CLI with its own path but spawns the bundle as cli.js. The exact-file realpathSync comparison treated these as different builds and blanked a valid same-install stamp, stranding skill subcommands on whatever qwen PATH resolves to. Compare dirname(realpathSync(...)) instead: entries in the same package directory are the same install. Add tests for the npm-layout sibling case and for symlink resolution (guarding realpathSync against future removal). --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
41f0e3ca5d
|
feat(channels): add Web Shell management support for GitHub and GitLab (#8310)
Some checks failed
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 17 (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
SDK Java / ubuntu-latest / Java 11 (push) Waiting to run
SDK Java / macos-latest / Java 21 (push) Waiting to run
SDK Java / ubuntu-latest / Java 21 (push) Waiting to run
Update ECS Runner Qwen / Update Qwen on ecs-update-64c (push) Has been cancelled
Update ECS Runner Qwen / Update Qwen on ecs-update-sg (push) Has been cancelled
* feat(channels): add Web Shell management support for GitHub and GitLab * test(channels): update channel-registry manageable expectation for GitHub and GitLab * test(web-shell): add gitlab manageable:false negative case for symmetry * fix(web-shell): add GitHub/GitLab platform marks, i18n labels, and empty-state copy * feat(web-shell): descriptor-driven groupPolicy, senderPolicy, allowedUsers for GitHub/GitLab channels * feat(web-shell): render field descriptions below inputs in channel editor * feat(web-shell): add reasonFilter and action_prompt_template fields with record kind support * fix(cli): validate string-list and record kinds in assertDescriptorValue The daemon-side store validation only accepted string, secret, boolean, number, and enum field kinds. Channels declaring string-list or record fields in their management descriptor (GitHub allowedUsers/reasonFilter, GitLab action_prompt_template) could never be saved through the Web Shell. Teach assertDescriptorValue the two new kinds and add store-level tests covering both acceptance and rejection paths. * fix(web-shell): prevent silent config rewrite and record crash in editor - initialFieldValue: for existing instances with an absent enum field, return empty string instead of the first option. This forces the user to explicitly choose rather than silently writing a new value on save. - isMissingField: guard Object.values().every() with typeof check so hand-edited configs with non-string record values show a validation error instead of throwing TypeError. * refactor(web-shell): extract hasDescriptorSenderPolicy helper, hide empty Access section - Extract repeated descriptor.fields.some(f => f.key === 'senderPolicy') into a shared hasDescriptorSenderPolicy() helper (was inline ×4). - Conditionally render the Access section: for descriptor-driven types with a non-pairing policy the section would show only a bare heading with no content beneath it; now it is omitted entirely. * fix(cli): accept undeclared record keys in assertDescriptorValue record field options are UI hints for which rows to render, not a closed set. The GitLab adapter resolves action_prompt_template by plain key lookup and accepts any GitLab todo action_name, but the store rejected keys outside the 9 declared options — silently bricking editing of configs that use server-side actions the descriptor doesn't enumerate. * fix(web-shell): harden channel editor parsing, defaults, and validation - Guard the three record JSON.parse sites with an isRecord shape check so a non-object payload can't throw during render or upsert. - Add an explicit descriptor `default` for enum fields and prefer it over option order when seeding a new channel, so an access-control default is declared intent rather than an emergent property of array order. - Declare the GitHub reasonFilter reasons as options and validate string-list input against them in the form, surfacing typos before submission instead of failing at channel start. - Fall back to an uppercased display-name initial (or '?') for the platform mark so an empty name can't render blank. * fix(web-shell): use distinct validation message for string-list option errors Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |
||
|
|
4379755474
|
docs(review): tell the agents drive and mock-provider exist, and how a fix's test earns its place (#8369)
* docs(review): tell the agents drive and mock-provider exist, and how a fix's test earns its place
Two commands shipped and merged with zero references in SKILL.md or the
briefs. An agent reading its brief had no way to know they were there, so
the verification they exist for went on being hand-written — which is the
state they were built to end.
The verify brief now carries both: what they serve, and — for `drive` —
that the ruling is on `outcome`, never on the captured text alone.
`not-ready` means nothing was driven, so nothing observed is evidence
either way; `timed-out` and `overflowed` mean the capture is partial, and
a partial capture is not evidence that the run produced nothing;
`unavailable` is an environment gap and not a finding. It says to pass
`--ready` for anything that binds a port, because without it an empty
capture reads as "the feature does not work" when it means "the daemon
had not finished starting". And it names what the request log is FOR: the
same drive against both trees, then a diff of the two request sequences.
Step 6B gains the one lesson from this stack of PRs that nothing already
covered. A test added with a fix has to fail without the fix, and
measured on this pipeline's own PRs, four assertions written to pin a
real defect all survived the mutation they were written for:
`toContain('"index":0')` passed with the tool-call index deleted because
that string is also on every `choices` entry; `toContain('input_json_delta')`
passed with the arguments handed over whole because the mutation kept the
type and changed the field; a `set +e` assertion pinned the mechanism
while `exit` was what broke it; a pure function tested alone passed while
the request path called a different one. All the same shape — asserting a
string is present rather than that the behaviour holds.
Deliberately NOT added: a lens on rules applied to one path and not its
sibling. That mistake was made three times across these PRs, and the
briefs already carry it as "the highest-value check in your slice, do it
first and do it exhaustively" — better stated than I would have restated
it. A second copy would be the drift this skill keeps fixing.
* docs(review): background the mock, then wait for its report
The paired mock-provider/drive example ran both commands sequentially in
one block. mock-provider serves for the whole --ttl and returns only when
it expires -- measured: with --ttl 3 the handler returns at 3.0s -- so an
agent copying the block verbatim gets `drive` starting 600 seconds later,
against a mock that has already shut down. That is a not-ready outcome or
an empty capture: the exact false negative the paragraph below it warns
about.
Background the mock and wait for its report before driving. The report is
written once the port is bound, so the file's appearance is a readiness
signal rather than a sleep -- which is the same discipline drive's own
--ready enforces, and the example should not have been guessing where the
command it documents polls.
|
||
|
|
d7d793c5bf
|
feat(serve): make sub-session concurrency caps configurable (#8341)
* feat(serve): make sub-session concurrency caps configurable The per-caller (5) and workspace-total (20) ceilings on concurrent create_sub_session sub-sessions were hardcoded, and 5 was too tight for legitimate parallel fan-out. Both are now configurable via serve.maxConcurrentSubSessionsPerCaller and serve.maxConcurrentSubSessionsTotal, with defaults raised to 16 and 24. The total default stays below the bridge's default maxSessions (32): finished sub-sessions linger in the session table until idle-reaped, so a total cap at the table limit would fail the next fan-out wave at bridge admission and starve interactive sessions of slots. Caps are a daemon-resource control, read from trust-filtered merged settings at launcher creation (primary, secondary, and dynamic workspaces); only positive integers are honored, anything else falls back to the built-in defaults. * fix(serve): clarify total-cap comment describes the default, not an invariant (#8341) * test(serve): pin sub-session cap schema defaults to runtime constants (#8341) * fix(serve): remove redundant optional chaining on non-optional parameter (#8341) * fix(serve): clamp total sub-session cap to tracked-id set size (#8341) The configurable maxConcurrentSubSessionsTotal could exceed MAX_TRACKED_SPAWNED_SESSIONS (1024), causing the depth-1 nesting gate's FIFO eviction to discard still-alive sub-session ids and break the single-level nesting guarantee. * fix(serve): surface sub-session cap fallbacks and schema ceiling (#8341) --------- Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> |
||
|
|
cb2555c7c5
|
feat(desktop): package Web Shell as a release-ready desktop app (#8132)
* feat(desktop): add Web Shell Tauri proof of concept * feat(desktop): prepare Web Shell shell for release * fix(desktop): make release dry runs portable * fix(desktop): harden cross-platform release smoke * fix(desktop): stabilize Windows and Linux CI * fix(desktop): scope bootstrap env to daemon * fix(desktop): stabilize packaged app smoke * fix(desktop): diagnose Linux packaged startup * fix(desktop): address release readiness review * fix(desktop): address follow-up review findings * fix(desktop): address runtime review blockers * fix(desktop): gate cookie auth acceptance behind desktop bootstrap flag - Cookie→Bearer translation middleware now only active when desktopShellBootstrap is enabled - Use timing-safe comparison for bootstrap token validation * fix(desktop): replace cookie handshake with URL fragment auth - Navigate the desktop WebView to /#token=<token>; the fragment never reaches the server, so drop the desktop cookie bootstrap middleware, its cookie->bearer translation, and the related serve tests - Skip the deferred-runtime auth gate for pre-auth Web Shell routes (GET|HEAD / and /assets/*): a document navigation cannot carry an Authorization header, so the fast-path window used to answer the first desktop navigation with 401 Unauthorized until a manual reload - Poll /health?deep=true before navigating: deep health stays 503 (reason: bootstrap) until the runtime app that mounts the Web Shell is ready, so readiness can no longer race the deferred window - Run the folder picker off the main thread and only store the runtime after the WebView navigation succeeds - Enable withGlobalTauri plus a bootstrap capability so the bootstrap page can subscribe to desktop lifecycle events - Update smoke-packaged to assert the fragment contract (unauthenticated root navigation 200, no cookies minted, API routes still 401) and sync the release design doc * fix(desktop): fix Linux smoke log path, add runtime .gitkeep, correct README (#8132) * fix(desktop): close release readiness gaps * fix(cli): keep deferred serve auth gate closed when web shell unmounted (#8132) * fix(desktop): address review feedback on auth gates and runtime bundle (#8132) - Cover the method guard in isPreAuthWebShellRequest: assert unauthenticated POST to / and /assets/* is still 401 during the deferred runtime window. - Add unit tests for is_allowed_navigation covering the unset origin, set origin, and bootstrap-after-origin cases. - Drop DEV:'true' from the release bundle step so the esbuild metafile is no longer shipped as dead weight in the desktop runtime. * fix(desktop): address review feedback on runtime extraction and release workflow (#8132) - Extract .zip Node archives with unzip so Linux cross-builds for win32-x64 no longer crash on GNU tar. - Build the Windows signing config with ConvertTo-Json instead of backslash escapes, which PowerShell treats as a parse error. - Fetch the runtime Web Shell without a bearer token so the smoke test exercises the pre-auth navigation path the shell relies on. - Make GitHub release creation idempotent so a re-run after a partial publish uploads assets instead of failing on the existing tag. * fix(desktop): normalize artifact filenames to prevent updater 404s (#8132) GitHub rewrites spaces to dots when release assets are uploaded, but the updater manifest encoded spaces as %20 via encodeURIComponent. This caused every platform's auto-update URL to 404 on published releases. Replace spaces with hyphens in the Collect artifacts step for all platforms so the local filename, the manifest URL, and the published asset name agree by construction. Update test-release.js fixtures to match and assert no artifact name contains a space. * fix(desktop): address review feedback on security, lint, and code quality (#8132) * fix(desktop): address review feedback on smoke test, error UX, and window state (#8132) * fix(desktop): address review feedback on crate build, recovery UX, auth gate, and CI (#8132) * fix(desktop): address review feedback on settings race, version script, and log growth (#8132) * fix(desktop): address review feedback on retry, auth gate, and release clobber (#8132) * fix(desktop): gate commands to bootstrap origin and show native update dialog (#8132) * fix(desktop): use matches! instead of PartialEq on JoinError result (#8132) * fix(desktop): wait for deferred runtime in smoke tests and sync release flags on clobber (#8132) --------- Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.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 Code Autofix <qwen-code-autofix@users.noreply.github.com> |
||
|
|
b4128ba8f5
|
test(review): pin drive's capped-stream case to its invariant, not to bash 5.2 (#8366)
The SIGPIPE fabricated-exit-code test asserted the one wrong answer CI's
bash 5.2 produces (the EXIT trap firing with rc=0), and failed on every
other shell. Measured, per version, same script, same pipe:
- bash 5.2 (CI ubuntu): rc=0 — fabricated clean pass
- bash 5.3 (homebrew macOS): empty — sentinel file created, write lost
- bash 3.2 (stock macOS): rc=1 — the echo's EPIPE error recorded,
plus a stray padding line leaked
into the sentinel file
Three shells, three different wrong answers. The assertion now pins the
one invariant they share — the script's real `exit 5` never survives the
cap — which is the design point the test exists to defend, and holds on
every bash instead of one. (A first draft enumerated the wrong answers as
an allowed set and was immediately falsified by the third shell; the
enumeration is a moving target, the invariant is not.)
Also reads the sentinel through existsSync like the suite's own realExit
helper, so a shell that never creates the file reports null instead of
throwing ENOENT.
|
||
|
|
548fb755b8
|
feat(review): mock-provider — the protocol is a fixture, the answer is yours (#8355)
* feat(review): mock-provider — the protocol is a fixture, the answer is yours
The single most-rewritten artifact in this repo's verification corpus:
94 hand-written mock servers across the maintainer sessions, median
3.3 KB each. What they share is the protocol; what they differ on is the
reply.
93% SSE framing 92% a `[DONE]` terminator
73% /v1/chat/completions 69% a usage block
62% a request log
Below that the agreement stops, and the split is not "how much mock to
ship" but which half. 43% emit `tool_calls` — and across those the SSE
SKELETON is unanimous (41 of 41 carry the chunk `index`) while the
CONTENT is not: thirteen distinct tool names, four appearing exactly
once, the most common at 29%. Request classification is 20%, scenario
switching 14%. So this command owns the framing and the caller's
responder owns the answer, which is the same line `build-test` and
`test-delta` already draw.
Two things it takes over that the corpus did by hand:
The port. 67 of 94 read one from an env var — a number someone chose and
hoped was free, so the second review on a machine collides and its
failure looks like the product's. This listens on 0 and reports what the
OS gave it.
The record. A request log is where an A/B gets its evidence: the same
drive against two trees, then a diff of the two request sequences. That
only works if both sides write the same shape, so it is JSONL here
rather than whatever a given session's mock happened to print.
One correction worth recording: an earlier pass over this corpus claimed
68% did error injection and that a `context_window` rule was a common
hardening. Both came from loose patterns. Measured properly, injection is
about 17% and the context rule appears in **0 of 94** — it was one
session's good idea, not a practice. Neither shaped this command.
* fix(review): bound the request, and number only what the responder saw
Round-1 findings on this PR, both measured before fixing.
`/v1/models` is answered without consulting the responder, but it still
took a request number: two calls with a models fetch between them handed
the responder `[1, 3]`. The field's own doc says "a responder that
answers the Nth call needs this", and a responder keyed on its third call
would have fired on its second.
Nothing bounded a request body. Measured: a 40 MiB POST was read whole
into memory, handed to the responder, and written to the JSONL twice —
once as the request and once inside the reply record — leaving an 80 MiB
log. `drive` had just grown an 8 MiB log cap for this exact hazard, and
this was a larger door beside it; `build-test`'s disk floors exist
because a review filled its own machine once already.
Over the ceiling is 413, not a trim. A truncated body parses to different
JSON, so the mock would answer a request the client never sent — a
behaviour difference introduced by the harness, which is the one thing it
must never introduce. What the RECORD keeps is trimmed instead, and says
where: an A/B needs the shape of the request sequence, not every byte of
every prompt. Same measurement after: 28 KiB.
* docs(review): say which protocol this fakes, and which tool covers the rest
The command read as more general than it is. It fakes ONE protocol — the
OpenAI-compatible chat API — and nothing in it is qwen-specific, so any
project whose product calls such an endpoint can drive against it. But
the protocol is also the boundary, and neither the module header nor
`--help` said so.
Of the 94 mocks measured: 73% spoke this protocol, 23% stood up the
project's own HTTP service, and single cases faked MCP/JSON-RPC, OAuth
and the Anthropic API. For those, the tool is `drive` — readiness,
completion and cleanup for any process a reviewer starts.
That is the same division the rest of this line draws, and worth stating
where a reader meets it: a protocol 73 files agree on is a fixture, a
service that differs across all 22 is a judgement. Said in the header,
in `--help`, and in the PR body in both languages.
* fix(review): a malformed reply from the responder HUNG the request
Round-3 finding, and the worst shape the failure could take.
The responder is the caller's module, and a caller's module is exactly
the kind of thing that returns `undefined` from a branch nobody took.
Measured, one probe per shape: `undefined`, `null`, a bare string, a
`status` that is not a number, and tool args holding a circular
reference each left the request with **no response at all**. Not an
error — a hang.
That matters more than a wrong answer would. The product under test sits
waiting, `drive` eventually reports `timed-out`, and a bug in the
harness has been presented as the behaviour of the diff. This whole line
of work exists to stop the harness manufacturing findings, and here it
was manufacturing the loudest kind.
Two quieter shapes went with it: `{}` and `{foo: 1}` answered 200 with
an empty completion, indistinguishable from a model that legitimately
said nothing, and `status: 999` was sent on the wire as an HTTP status.
Every reply is now checked before it is used, and a bad one is a 500
naming what the responder returned and what it should have. Nine
malformed shapes, nine immediate 500s; the two valid shapes are
untouched. Pinned by comparing the whole result table, so a regression
names which shape broke rather than just failing a count.
* feat(review): speak Anthropic too, in the shapes this repo's own client parses
Requested after I argued against it, and the argument was thin. I read
the corpus — one Anthropic mock in 94 — and treated that as demand. The
product says otherwise: `anthropicContentGenerator/` is 11,618 lines and
three PRs touched it in the last three months, #8163 and #8166 among
them. One of those was settled by posting the two branches' actual bytes
to a real Anthropic endpoint and reading back 400 versus 200. "Only one
mock exists" does not mean nobody needed one; it means faking it by hand
was harder than finding a real endpoint, which is the case FOR shipping
it rather than against.
The shapes are taken from this repo's own generator, not from memory:
the six SSE events it parses, `input_json_delta` for streamed tool
arguments, the four usage fields including the cache counters, and the
block-array content. A mock whose protocol came from recollection would
be testing the recollection.
Three differences that a from-memory version gets wrong:
`/v1/messages` ends on `message_stop` and sends no `[DONE]` — that is
OpenAI's terminator, and a client waiting for it hangs, which is the same
failure this command just fixed on the responder side.
Tool arguments stream as `partial_json`, not as a finished `input`
object; a client that accumulates them and one that expects them whole
behave differently.
The system prompt is a top-level `system`, not a message — and "the
system prompt says verifier" is how 20% of the corpus classified its
requests, so a responder branching on prompt text would never see it.
The wire is recorded per request as well as passed to the responder: an
A/B whose two sides dialled different endpoints is not comparing the same
thing, and the log is where that shows.
Two of the four guarantees were pinned by assertions that did not pin
them — `toContain('input_json_delta')` survives handing the arguments over
whole, and testing `anthropicText` as a pure function survives the
request path never calling it. Both are parsed and driven now, and all
four fail a mutation.
* fix(review): stop answering requests this mock has no business answering
Round-4 findings, both measured.
A call to `/v1/embeddings`, a GET to the chat endpoint, a non-JSON body
and an entirely empty body each came back as a plausible 200 completion.
So a product that dialled the wrong endpoint, used the wrong method, or
sent a broken payload looked, from the review's side, like it was
working. That is the mock concealing the exact defect the review exists
to find — worse than any wrong answer it could give. Each is a 400 now,
saying which of the three it was, and the refusal goes in the record so a
run that dialled wrong can be seen afterwards.
The log bound from round 1 only held half. `text` was trimmed while
`...mreq` spread the parsed `body` into the same entry carrying the
identical payload: a 200 KB system prompt gave an 8 KB `text` and a
205 KB log. The record now summarises the body by its keys rather than
copying it — the evidence an A/B needs is the request's shape, not every
byte of every prompt. Same measurement after: 9.2 KiB.
Four probes in this round, four combinations that were never exercised:
Anthropic non-stream tool use, empty text on both wires, and the four
malformed requests above. The first three were already correct.
* fix(review): a number is spent only when the responder is actually asked
Round-5 finding, and the second pass over something round 1 thought it
had closed. Round 1 stopped `/v1/models` from taking a request number.
Running all four rounds' fixes together on one mixed sequence showed the
other two ways it still could — the record read `[1, 2, 2, 3, 4, 5]`:
the models call REUSED the previous request's number, having none of
its own to report;
a refused `/v1/embeddings` incremented past it, because the refusal
added in round 4 lands after the counter.
A responder keyed on its Nth call reads a sequence like that and fires on
the wrong request — which is exactly the failure round 1 set out to fix,
surviving in two shapes it had not looked at.
The counter now moves only for a request that will reach the responder,
and the record carries `null` for everything else rather than a number
that would read as "the responder handled this". `Responder` takes a
`RespondedRequest` where `n` is non-null, so the invariant is in the type
rather than in a comment.
Measured after, same sequence: the responder sees `[1, 2, 3]` and the
record reads `[1, null, 2, null, null, 3]`.
* test(review): pin the invariants across a mixed sequence, not one path at a time
Round-6, and a response to what round 5 taught rather than a new defect:
every fix so far was right on its own, and the counter was wrong across
them. A single-path assertion cannot see that — it exercises one route
and stops.
So this drives 120 requests over every known path, in a deterministic
pseudo-random order, and checks what must hold whatever the order is:
one record per request; the responder asked exactly for the requests
that reach it, numbered 1..N with no gaps; the record's numbering
agreeing with the responder's; no entry carrying the parsed body however
large the request; every entry naming a valid wire.
It earns its place by catching what the single-path tests did not.
Restoring each of three historical bugs — round 5's `!isModels`
increment, round 4's missing refusal, round 1's copied body — turns this
one test red on its own.
Nothing was broken this round: all seven invariants held before it was
written. It is here so the next stack of fixes cannot quietly break each
other the way round 5's did.
* fix(review): four findings from a /review run on this PR
All four hold, and each names something six rounds of my own review had
not looked at.
R1-4 is the one that matters. `replyProblem` guarded the tool branch's
`args` against a circular reference and left the status branch's `body`
unguarded — so `{status: 500, body: circularObj}` passed validation, and
`record()`'s `JSON.stringify` then threw "Converting circular structure
to JSON" as an unhandled rejection: the request hung and the drive around
it timed out. Confirmed by probe before fixing. Whether a reply can be
serialised is a property of the reply, not of the branch it arrived on —
the same one-directional reasoning round 5 caught in the counter.
R1-1: the `describe` still said "OPENAI-COMPATIBLE endpoint (that
protocol only)" after `/v1/messages` was added. A user reading `--help`
concludes the command cannot serve an Anthropic-wire product and
hand-writes a second mock. Pinned against the ROUTES the implementation
serves rather than against a sentence, so a third one added without
saying so fails the test instead of misleading someone quietly.
R1-2 and R1-3: the `--responder` module loader and the CLI handler had
zero coverage — every test passed `respondOverride` and called
`startMockProvider` directly, so neither branch had ever executed. The
review found the `?? mod.default` fallback survives deletion with every
test green; a caller writing `export default function respond` would be
told their module exports no `respond` function. Both are driven now,
through fixtures in the repo rather than a temp file, because vitest
cannot import a module from outside the project root — which is why
these paths went untested in the first place.
The handler test asserts the TTL in seconds by elapsed time: dropping
the `* 1000` turns a 600-second TTL into 0.6, and the mock would exit
before the product connects, which the drive would report as a product
that never answered.
|
||
|
|
4338120100
|
feat(serve): resolve and report the daemon memory budget (#8245)
Some checks are pending
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 1/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 2/3 (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none - shard 3/3 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 1/2 (push) Waiting to run
E2E Tests / E2E Test - macOS - shard 2/2 (push) Waiting to run
E2E Tests / channel-plugin E2E (nightly) (push) Waiting to run
E2E Tests / cron-interactive E2E (nightly) (push) Waiting to run
E2E Tests / web-shell Browser Regression (push) Waiting to run
SDK Java / ubuntu-latest / Java 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
* feat(serve): resolve and report the daemon memory budget The daemon has no notion of how much memory it has. It samples its own RSS and heap every five seconds, and polls the primary ACP child's RSS, but there is no limit anywhere to divide those by: no cgroup read, no heap-size limit, no ratio, no `limits.*` memory field. Every number it reports is an absolute byte count with nothing to compare against, so "how close to exhaustion is this daemon" cannot be answered from `/daemon/status` at all. Resolve one set of figures at boot and report them. Configured and effective budgets are separate: the effective value is capped at resolved cgroup or host memory, so an operator passing a budget larger than the machine gets a denominator the machine can actually back, with the discrepancy visible rather than silently resolved. A derived budget below the documented minimum is reported as `insufficientMemory` rather than clamped upward, which would have invented capacity that does not exist — a 768 MB host would otherwise report a 1 GB budget and poison every ratio computed from it. `limits.memory` carries the static figures, including `legacyCeilingMb`: the ceiling an ACP child receives today with no budget involved, so the gap between current behavior and any future policy is measurable before that policy exists. `runtime.memory` carries live counts and the advisory per-child share at both the registered and the live child count. Nothing here sizes a child. Dividing the pool by a workspace count is not a sound policy on its own, and the advisory shares exist to show why: on a 32 GB host with 25 registered workspaces and only the preheated primary live, a registered-count divisor would cut that child from 16384 MB to 614 MB for memory no dormant workspace is holding, while the per-child floor still lets 25 children authorise more than the pool. Registration is not allocation; a real policy needs admission at spawn time keyed on concurrently live children, and it needs this data to be designed against. Applying such a share is also a compatibility change even with no refusals, since it alters child GC and OOM behavior — so it is not something this change should slip in under the heading of reporting. Refs #8182 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): report honest memory counts and guard the registered share (#8245) * fix(serve): reuse workspace snapshots when counting active children Counting active ACP children from `listManaged()` is right — `list()` only returns entries in `active` state, so a workspace mid-drain, mid-replacement, or blocked still holds a live child that `list()` drops. But taking the count by calling `getDaemonStatusSnapshot()` again per managed runtime undoes the existing reuse of the primary bridge's snapshot, and `getDaemonStatusSnapshot` rebuilds the whole session array on every call. The second pass also reads the tree at a different instant than the rest of the response, so `activeAcpChildren` could disagree with the session and channel figures beside it. Reuse the snapshots already taken instead, keyed by bridge, and fall back to a fresh call only for a managed runtime the first pass missed — which is exactly the non-`active` entry the `listManaged()` change exists to catch. The reuse this restores was already guarded by a test asserting one snapshot call per bridge, but that test resolves no memory budget, and the second pass ran only on the budget path — so it stayed green while every production `/daemon/status` call did the work twice. Added a case that resolves a budget and asserts the same property; it fails against the previous commit with "expected 1 times, but got 2 times". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(serve): address review feedback on daemon memory budget (#8245) * fix(serve): import isValidMemoryBudgetMb in serve command (#8245) * fix(serve): address review feedback on daemon memory budget (#8245) * fix(serve): stub isChannelLive on serve test fake bridges (#8245) * fix(serve): address review feedback on daemon memory budget (#8245) - Make the stderr-gate test host-independent by pinning os.totalmem through a vi.mock toggle instead of reading the runner's cgroup - Add a spawn-path constant parity test enforcing that getAcpMemoryArgs and legacyChildCeilingMb agree on the fraction and cap, converting the comment-only invariant into a test - Narrow the mirror comment to name only the two constants that actually have spawn-path counterparts - Accept availableMemorySource through the resolveDaemonMemoryBudget seam so the constrained path is testable end-to-end - Report maxChildHeapMb alongside minChildHeapMb on the wire so clients can distinguish the 16 GB cap from a large host - Move the listManaged/list comment to the computation it describes - Add a cross-reference at the opts literal for the late-assigned daemonMemoryBudget field * fix(serve): address review feedback on daemon memory budget (#8245) * fix(serve): address review — split fraction constant, sharpen parity test, deduplicate error, populate bootstrap memory (#8245) * fix(serve): address review — document maxChildHeapMb, make parity test order-independent (#8245) * fix(serve): address review — split parity test into two files to avoid cold re-import timeout (#8245) * fix(serve): address review — correct session count, compatibility scope, bootstrap memory docs (#8245) * fix(serve): address review — align help text framing, document default cap, fix stale comment (#8245) * fix(serve): address review — add positive memory-budget validation test, correct activeAcpChildren docs (#8245) * fix(serve): address review — document childRssBytes stale tail after watcher detach (#8245) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Qwen Autofix <qwen-autofix@users.noreply.github.com> Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
576918c7d2
|
fix(cli): allow pasting sensitive extension settings (#8342)
* fix(cli): allow pasting sensitive extension settings * test(cli): cover sanitized secret pastes * test(cli): cover appending pasted secrets * test(cli): cover pasted ASCII boundaries |
||
|
|
c0f5bed530
|
feat(review): drive — readiness polled, completion proven, cleanup guaranteed (#8349)
* feat(review): drive — readiness polled, completion proven, cleanup guaranteed The highest-yield review technique in this repo's history is the local build-and-drive verification: build the PR, run the real product, watch what it does. Across 260 of those sessions the mechanical half is the same every time, done by hand every time, and two of its three steps are done by GUESSING: 81% waited with `sleep N`; only 36% polled for readiness. A `sleep 2` that lands before the daemon binds its port makes the capture return an empty screen, and an empty screen reads as "the feature does not work". That is a false negative manufactured by the harness, and it is silent. 74% captured one screenful with no way to know the command had finished. A capture taken mid-write is a partial observation presented as a complete one. 87% cleaned up by hand with `pkill -f <a name they made up>`. What one round leaks, the next round inherits and captures. `drive` owns exactly those three - ready or not, finished or not, gone either way - and nothing else. What to drive, and what the output means, stay with the caller: the same split `build-test` and `test-delta` draw. Two bugs only a real run could show, both green under the unit tests: `pipe-pane` races the script. `new-session` starts it immediately and the pipe attaches after, so a fast drive finishes - taking its session with it - before the pipe exists. Measured: a one-second delay makes `pipe-pane` itself exit 1 and the log stay empty, which this command reported as `timed-out`. A pane is a window that closes; the script's own redirect is the record. The sentinel was a trailing `echo`, which `exit N` never reaches - and `exit N` is how a drive script reports its result. Measured: `echo failing; exit 17` came back `timed-out` with a null exit code, a run that answered in milliseconds reported as one that never finished. `set +e` has no bearing on `exit`; a `trap … EXIT` does, and covers falling off the end, an explicit exit, and a `set -e` abort alike. The test that should have caught the second asserted `set +e` was present - the call shape, not the behaviour. Replaced with tests that drive real bash through all four exit paths. * fix(review): --server was a path and a shell word, and safe as neither Round-1 finding on this PR, both halves reproduced before fixing. `--server '../../PWNED'` put `drive.sh` and its log at the FILESYSTEM ROOT: `join(tmpdir(), 'qwen-review-drive-' + server)` normalises the `..` away, so the name escapes the temp dir it is supposed to sit under. And a name holding `;` splits the `bash <script> > <log>` line tmux runs into further commands - the constructed line reads `bash /tmp/…-a; touch /tmp/X; b/drive.sh > …`, which is two commands and a fragment. The value is operator-supplied today, which is why this is a hardening and not a live hole. But the command exists to be called from a review that builds its arguments programmatically, and a server name derived from a branch or a PR title is one step away from here; neither of those is ours to trust. Restricted to a charset that cannot be either - letters, digits, dot, dash, underscore, 64 max - and the paths are quoted anyway. The redundancy is the point: whoever widens the charset later should not also have to notice the shell line. The quoting itself shipped wrong first: hand-escaping `'\''` through a test file produced `'''`, which bash answers with `unexpected EOF`. It is asserted by round trip through real bash now - six shapes including a bare quote, a semicolon and a backtick - rather than against a hand-written expected string, because a hand-written expected string is what got it wrong. * fix(review): the poll waited by shelling out, and could stop waiting Round-2 finding. The readiness and sentinel loops paced themselves with `sleep 0.25`. Fractional operands are a GNU/BSD extension - POSIX specifies an integer - so on a system without it `sleep` fails, returns instantly, and both loops go tight. Measured through the exec seam, before the fix: **8,196,280 readiness probes in one second**. That is not a spinning CPU, it is the probe hammering the daemon it is waiting for at millions of requests a second and then reporting that the daemon never became ready. A false negative manufactured by the harness - which is the precise failure this command was written to remove, arriving through the wait it uses to remove it. `Atomics.wait` blocks for a real duration with no subprocess and no platform surface. Same measurement after: 5 probes/sec. Pinned by rate rather than by mechanism: the test counts probes per second through the seam and requires the figure to stay in the tens, so it fails for any wait that does not actually wait - including one that goes through the seam at all. * fix(review): bound the log by watching it, not by capping the stream Round-3 finding, and the first fix for it was worse than the bug. `trimCapture` bounded the string this command returns; nothing bounded what the driven script wrote. Measured: 200k lines left a 9.9 MB log while the report stayed at 200 KB — and a drive script is whatever the reviewer wrote, so there is no ceiling at all. This repo has paid for that once already: `build-test`'s disk floors exist because an `npm ci` filled the disk 33 seconds in and then failed every agent after it. The first attempt piped the drive through `head -c`. Measured, not assumed: `head` exits at the cap, the writer takes SIGPIPE mid-loop, and the EXIT trap fires with `$?` from the last successful echo — so a script whose final statement is `exit 5` reported **rc=0**. Not a lost verdict: a fabricated one, a failing run presented as a clean pass. The comment I had written for that fix claimed the sentinel survived; running it took four seconds to disprove. So the size is watched instead, in the poll that was already running, and a drive that crosses the cap is stopped and reported as `overflowed` — its own outcome, with no exit code, because a run this command had to stop is not a run that finished and inventing a code for it is the whole failure above. The sentinel also moved to its own file. Two facts, two channels: the log may be trimmed, the verdict may not. The `head -c` behaviour is pinned by a test that asserts the fabricated `rc=0`, so the shortcut cannot be reintroduced by anyone who reasons about it instead of running it. * fix(review): the scratch tree outlived every run that made it Round-4 finding, and the other half of round 3's measurement. The log size was bounded; the directory holding it was not removed. The default server name carries the pid, so every invocation leaves its own tree under the temp dir — measured across this PR's own end-to-end runs, six drives left five directories behind. Removed in the same `finally` that kills the server, and for the same reason: the report is fully in memory by then, so nothing the caller needs is still in there. A caller who passed `--log-path` owns that file and keeps it. All six outcome paths re-driven for real afterwards — completed rc=0, completed rc=17, a `set -e` abort, a timeout, a readiness probe that passes, and one that never does — with zero tmux servers and zero directories left behind. |
||
|
|
f612b3aa38
|
feat(review): teach the verifier the falsify-not-verify asymmetry (#8346)
* feat(review): teach the verifier the falsify-not-verify asymmetry The verify brief already carried the one-way, quote-the-contradiction bar on rejecting a Critical. What it did not name are the two states that reliably masquerade as grounds for rejection and are not — the negative list a production reflection filter (run over reviews at millions-of-comments scale) used as its single operating rule: - "I could not verify it." A trace that fails to confirm is information about the trace, not the claim; the floor is confirmed (low confidence) with what-would-settle-it named. - "Its evidence is somewhere I did not look." The finder may have grepped a caller the diff never touches, fetched issue evidence, observed a run. The verifier has the same tools, so it must go read the claimed source first, and reject only on contradiction; a genuinely unreachable source floors at the downgrade, never rejection. Both are stated with the counter-balance explicit: confirming also requires the trace, and the rule forbids the shortcut only in the rejecting direction, because that direction is the irreversible one. SKILL.md's Step 4 summary of what the brief holds names the asymmetry so the orchestrator knows what a verdict means. Tests: full review suite, 1369 assertions green. * fix(review): clarify the falsify-not-verify rejection carve-out and pin it in tests --------- Co-authored-by: qwen-code-ci-bot[bot] <qwen-code-ci-bot[bot]@users.noreply.github.com> |
||
|
|
8d6d2ab56a
|
feat(cli): /summary supports custom export path (#8116)
* feat(cli): /summary supports custom export path (#8113) `/summary` now accepts an optional path argument, matching `/export`'s behavior. When a path is provided, the summary is saved there instead of the default `.qwen/PROJECT_SUMMARY.md`. - `/summary` → saves to `.qwen/PROJECT_SUMMARY.md` (unchanged) - `/summary docs/summary.md` → saves to `docs/summary.md` - `/summary /absolute/path/summary.md` → saves to absolute path - `/summary docs/` → saves to `docs/PROJECT_SUMMARY.md` If the path is a directory (existing or ending with `/`), the default filename `PROJECT_SUMMARY.md` is appended. Parent directories are created automatically. * fix(cli): summary custom path dir detection and i18n key (#8116) * test(cli): assert relative display path in summary tests (#8116) * fix(cli): summary path containment, early validation, and mkdir hardening (#8116) * fix(cli): defer summary mkdir to save time so failed generation leaves no empty dir (#8116) * fix(cli): normalize path separators in summary test and assert file content (#8116) * test(cli): assert directory permission mode in summary test (#8116) * fix(cli): resolve symlinks in summary path containment check (#8116) * fix(cli): reject broken symlinks escaping project root in /summary (#8116) * fix(cli): guard /summary overwrite and expand tilde in path (#8116) * fix(cli): re-validate appended default filename for symlink escape in /summary (#8116) * fix(cli): harden /summary symlink chain walk, file mode, and overwrite guard (#8116) * fix(cli): address review feedback on /summary custom path (#8116) - Fix CRLF false-negative in overwrite guard by normalizing line endings - Allow overwriting empty pre-created files (zero-length bypass) - Detect trailing separator on existing file and report clearly - Log chmod failures via debugLogger matching exportCommand convention - Add comment explaining mkdir mode asymmetry - Update docs: /summary usage table and custom-path welcome-back note - Add i18n key for trailing-separator error in all 9 locales - Add tests for CRLF, empty file, and trailing separator cases * fix(cli): address review feedback on /summary custom path (#8116) - Skip symlink-escape check for the default .qwen/ target so a symlinked .qwen/ directory (shared team config, overlay mounts) keeps working, and the check no longer runs after the LLM call - Re-run the overwrite guard immediately before writing to close the TOCTOU window across the slow generation step - Determine isDefaultTarget by comparing the resolved path against the default so `/summary .qwen/` gets the same 0o700 permissions - Only chmod 0o600 on file creation; preserve existing permissions on regeneration - Return empty content in interactive-mode errors to avoid double rendering (failInteractive already adds the error to history) - Tighten the overwrite-guard regex to require `**Update time**: ` after the Summary Metadata heading, preventing false positives - Fix the realpathNearestExisting comment to document the missing containment-during-walk guard vs export/stats copies - Add tests: symlink cycle, default target with symlinked .qwen, TOCTOU overwrite guard, explicit .qwen/ permissions, chmod preservation, interactive error content, regex false-positive * fix(cli): address review feedback on /summary custom path (#8116) * test(cli): cover post-LLM symlink re-check and interactive error UI (#8116) * fix(cli): address review feedback on /summary custom path (#8116) --------- Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com> Co-authored-by: Qwen Code Autofix <qwen-code-autofix@users.noreply.github.com> Co-authored-by: Qwen Code Bot <qwen-code-bot@users.noreply.github.com> Co-authored-by: qwen-code-ci-bot <qwen-code-ci-bot@users.noreply.github.com> Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com> |
||
|
|
1d7edbe0c0
|
fix(review): a mutant whose own test was red is not a survivor either (#8345)
Found by running /review against PR #8213. Six hunks in `packages/acp-bridge/src/bridge.ts` were correctly held at `inconclusive` because `bridge.test.ts` never ran green in the unmutated baseline, while eight mutants in the SAME file were scored `survived` and shipped as findings on the strength of other files' tests passing. A mutant runs against `greenProbes` only, so a red collocated test is excluded from the run - and "every affected test still passed" is then computed over a set that omits the one test most likely to catch the deletion. That is exactly the inference the hunk loop refuses, in its own words: the other probes passing shows only that THEY do not cover it, not that nothing does. The comment under the hunk guard shows how the gap survived review: it framed the asymmetry as "mutants guard the killed direction, hunks guard the survived one". True of an inconclusive RUN, and it left a mutant's survived direction unguarded against an absent covering test. Two separate guards, one of which was read as the whole rule. Checked before the budget, so a candidate that cannot yield a verdict does not spend a suite run to say so. The regression test carries both halves - a file whose own test is red, and one whose own test is green - because a guard that swept up the second would be no better. |
||
|
|
06ead8f4ff
|
fix(core): unblock history pagination on oversized transcript turns (#8335)
* fix(core): ride indivisible transcript pages over the read budget Backward history pagination dead-ended with HTTP 413 whenever a single turn exceeded the 4 MiB page budget: a turn cannot be split across pages, so the reader threw SessionTranscriptPageTooLargeError and the Web Shell latched a permanent pagination error banner. Take at least one indivisible unit per page (one aggregate record forward, one turn backward) so pagination always makes progress; the 32 MiB response serialization cap remains the hard ceiling. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * feat(web-shell): add retry button for history pagination error A non-retryable transcript page failure (4xx, partial replay) latched paginationError with no in-UI recovery short of reloading the session. The banner now offers a retry that force-clears the latch and refetches the same page, whose cursor was never advanced by the failed attempt. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> |