mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-11-07 06:19:43 -10:00
main
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a32684604a |
fix(core): surface Anthropic refusal details (#16079)
* fix(core): surface Anthropic refusal details Propagate Anthropic refusal stop_details through candidate finishMessage and the Finished event so both terminal UIs can show the provider's refusal category instead of a generic empty safety stop. Co-authored-by: Qwen-Coder <[email protected]> * style(core): apply Prettier to the refusal-detail diff The incremental lint gate selected 12 changed files and Prettier rejected four of them; this commit only re-wraps those lines, no behaviour change. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvygjkzk7s * fix(core,cli): scope the refusal notice to safety stops Review follow-up on #16079: - Both renderers now prefer `finishMessage` only for `SAFETY` stops. The field is a provider wire field (`@google/genai` types it), so a Gemini turn carrying it could otherwise replace the curated notice for `MAX_TOKENS` and even add an info line on `STOP`. - The refusal category lookup uses an own-property check: a provider `category` of `__proto__` / `constructor` now falls back to the generic notice instead of returning an object that renders as `[object Object]`. - `llm-chat` deletes `finishMessage` together with the deferred `finishReason`, so the stream no longer yields a notice with no reason. - Tests: category table (all mapped categories + prototype key + unknown) and a non-safety renderer case pinning the curated notice. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw651acyim --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
708ba347d3 |
feat(ci): add disposable external PR runners (#16513)
* feat(ci): add disposable external PR runners * test(ci): align platform lanes with disposable runner routing --------- Co-authored-by: yiliang114 <[email protected]> |
||
|
|
1762efe4cb |
fix(channels): keep a delivered turn completed when the notice send fails (#16512)
* fix(channels): keep a delivered turn completed when the notice send fails The stacked-skill turn notice is delivered after the answer, inside the same try block that guards the delivery. A failing notice send (for example a QQ 429 RATE_LIMITED DeliveryError) bubbled into the catch that emits failed(phase: 'delivery') and rethrows, rewriting an already delivered turn into a failure even though the user received the answer. Guard the notice send and log the failure to stderr instead, matching the deliveryStarted invariant: once delivery started, the turn's outcome is fixed. Refs #16441 * fix(channels): fail the turn when the notice was its only delivery The prompt result is `turnNotice || body`, so with an empty body the notice send is the turn's only delivery. Swallowing its failure reported the turn completed while nothing reached the user; rethrow so the outer catch emits failed:delivery, as main did. Pin the notice-only branch and the log text. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw5ylt0cid * fix(channels): log which notice was dropped when its send fails R2-1: the catch that keeps the turn `completed` after a failed notice send logged only the transport error. The notice text had already been sliced out of the answer, so the log line was the last place that knew what the user never received. Include it bounded and sanitized, matching the file's logging convention, and pin it in the existing regression test. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw651acyim --------- Co-authored-by: zzj <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4c5820c745 |
fix(web-shell): close out managed-host review threads from #15928 (#16203)
* fix(web-shell): close out managed-host review threads #15928 merged with 19 review threads left open. This lands the code fixes and the tests that actually pin them. Code: - shadowDom: the ref-counted release is now idempotent, so calling one install's cleanup twice no longer strips a sibling shell's stylesheet (R2-4). - ManagedArchivedSessions: the host status callback is held in a ref, so a host that stores the status in state no longer re-emits on every render and loops (R2-7). - ManagedSessionsPage: the pane measurement re-runs when the host supplies a session id after mount, so a host that starts in the waiting state still gets a ResizeObserver and the inline task panel (R1-2). - ManagedSessionsPage: the pending-owner link goes through openSession, so it clears the draft/error of the conversation being left and forwards the owner's workspace id (R1-13). - ManagedAgentWebShell: `baseUrl` is optional when `managedAgentProvider` is injected, and a missing one fails loudly instead of resolving requests against `<origin>/undefined` (R1-11). Tests: - shadowDom: a double release keeps the sibling sheet (R2-4). - ManagedArchivedSessions: a fresh inline callback settles (R2-7). - ManagedSessionsPage: pane measurement after a late session id (R1-2), pending-owner navigation carries the workspace (R1-13), the `:host:` pending key keeps sessions apart (R1-4), the empty-state test uses the real failure shape and asserts banner copy instead of a bare truthiness flag (R1-5), the welcome block is asserted absent next to a failure banner (R1-8), and a foreign error banner is dropped on session switch (R1-9). - ManagedAgentWebShell: no provider and no baseUrl fails loudly (R1-11). - managed-host.dom: the pending row assertion matches the row's actual copy instead of a substring it no longer contains (R1-7). Every new assertion was verified red with the fix removed. * fix(web-shell): keep the pending owner's workspace and late status callbacks Three review follow-ups from the #15928 threads: - The pending-owner link resolved the owner's workspace from the served session page, which is filtered, paginated and empty in host mode. When the owner was absent the click forwarded no workspace and the host's handler then cleared the selection. Capture the workspace id on the pending record at submit time and prefer it, keeping the page lookup as the fallback for records written before this change. - The `baseUrl` guard tested truthiness, so `baseUrl=""` — a supported same-origin value that resolves to the page origin — threw during render. Only a missing prop is a configuration error now. - Holding the archived-status callback in a ref keeps the loop closed but also dropped delivery to a host that supplies the callback only after the list settled. Re-emit on the absent -> present transition while keeping identity churn from re-running the effect. Each case has a regression test that fails without its fix. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw0nuit6aw * style(web-shell): apply prettier to ManagedArchivedSessions test The Lint & Static check fails on prettier formatting of the late-status callback's parameter type annotation. Formatting-only change; eslint and prettier now pass on the file. * fix(web-shell): close out managed-host R2 review threads - reject a null baseUrl, not only an absent one: a plain-JS host erases the prop type, so an unresolved config value arrives as `null` and every request resolved against `<origin>/null` - state the requirement in the README and both design-doc languages, which still promised that omitting the provider preserves Java behavior - pin the pending-record write half (the submitted workspace) and the readPending rejection of a non-string workspaceId; both tests fail when the guarded line is removed Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw0s4ubab2 * fix(web-shell): render the shell fallback for a missing baseUrl and pin banner clearing The `baseUrl` guard throws from the shell's own render body, above the ErrorBoundary it returns, so a host mounting before its config resolves saw an empty container instead of RootErrorFallback. The provider now resolves one level below an outer boundary, and the pending-owner navigation test also pins the `setError(undefined)` half of openSession. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw4plfohgl * fix(web-shell): name the shell fallback and assert the contained baseUrl failure The outer config boundary returns an unnamed arrow, which `react/display-name` rejects, so pin its name. The two baseUrl-guard tests still asserted the pre-boundary contract (the guard's throw escaping to the host); the boundary now reports that failure through the shell's own fallback, so assert both halves: the fallback renders and the guard's message still reaches `console.error`. The pending-owner test's added banner pin is reverted: the navigation error renders only when `interactive` is false, so that path cannot show it. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw4rqlfigo * style(web-shell): apply prettier to ManagedAgentWebShell The Lint & Static lane failed at "node scripts/lint.js --prettier" on packages/web-shell/client/ManagedAgentWebShell.tsx: the props interface `extends` clause and the RootErrorFallback JSX needed reflowing. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw52gecrh3 * docs(web-shell): describe the baseUrl guard's observable behavior The guard runs below the shell's own error boundary, so a host sees the fallback render and reads the cause from console.error -- never a throw. Correct the three copies of that contract and cover the resetKeys recovery path (missing baseUrl, then a present one) with a transition test. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw56qpv9h9 * test(web-shell): pin same-origin storage key and the provider reset path R5-1: assert the constructed provider's storageKey resolves against the page origin, so coercing `baseUrl` to undefined can no longer keep the test green while requests resolve against `<origin>/undefined`. R5-2: add a provider-only re-render case so the boundary's provider reset key is pinned, not just the `baseUrl` half. R5-3: document that recovery needs a re-supplied `baseUrl` or provider, because the boundary clears only through its reset keys. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw5q15wki1 --------- Co-authored-by: root <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
5faa620af9 |
fix(core): keep the user's instruction last in the model request (#16274)
* fix(core): keep the user's instruction last in the model request
A fresh turn's user entry is assembled from independent sources: per-turn
reminders ahead of the prompt, then hook/Mod context and the memory catalog
behind it. A long multi-part instruction therefore lands mid-entry with tens
of kilobytes of injected context on both sides, and a flash-class model
answered that it had received no task while the instruction sat there.
Reorder the request copy at the single point where every main-turn request is
assembled, moving the injected run that trails the caller's own text ahead of
it. Stored history keeps its shape: the transcript display projection and the
resume receipt both read the hook block as the final part, so reordering the
stored entry would break the projection and re-deliver hook context every turn.
Leading tool results keep their position, a non-leading tool result leaves the
entry untouched, and the volatile reattach tail stays last so the provider
cache breakpoint keeps its anchor. A reminder the caller placed between two of
its own parts bounds the movable run and stays put.
Refs #16259
Co-authored-by: Qwen-Coder <[email protected]>
* docs(core): correct the prompt-context placement prose
The Constraints bullet and the isInjectedPromptContext docstring both claimed a
caller-authored part shaped like a reminder keeps its own order. The rebuild
classifies by shape across the whole entry, so that promise does not hold for
the caller's last part: a trailing part in either envelope is treated as
injected and moves ahead of the caller's text. Describe the mechanism instead,
in the docstring and both design docs.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw1upqc7cl
* fix(core): reorder injected context on every user entry
The send boundary reordered only the pending entry, so request N sent
[reminder, hook, catalog, ...prompt] while request N+1 rebuilt the same
entry from stored history as [reminder, ...prompt, hook, catalog]. The
shared provider prefix then ended at the leading reminder and every
injected block was re-prefilled at full input price on every turn - the
failure mode REATTACH_BOUNDARY_METADATA exists to prevent (#11627).
Applying the same rule to every user entry in the request copy keeps an
entry's wire shape position-independent, so request N+1 extends request
N's prefix exactly. Stored history is still untouched, which keeps the
transcript projection and the resume receipt intact.
Pin it with a two-turn test that asserts the first turn's entry is
re-sent byte-identically, and compare each request with the previous one
instead of comparing the request prefix with stored history.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw25fj8od0
* docs(core): state the reorder's every-entry scope and name the third updated assertion
The every-entry pass landed in
|
||
|
|
5890329390 |
fix(ci): probe the host egress proxy so mainland release hosts can build the sandbox image (#16281)
The docker-container driver's buildkitd inherits neither the runner's proxy env nor the host network namespace. On ecs-qwen-hz1-17/18 the DNS answer for registry-1.docker.io is poisoned, so the pinned node:22-slim resolution dies with a direct-dial timeout before any application step — five consecutive image-build failures (#16039, #16162) that forced ECS_RELEASE_HOST_RUNNER_ENABLED=false. Probe the fleet-managed proxy marker and inject network=host plus env.*_PROXY driver opts only when the proxy answers a live registry probe. Hosted and HK runners have no marker file, so the probe emits nothing and their builds keep the default direct route. Verified live on hz1: unopted builder reproduces the incident, opted builder resolves the pinned digest for both platforms. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
f087e699af |
fix(core): register promoted initial commits in unborn repos (#16081)
* fix(core): register promoted initial commits in unborn repos A promoted command captured its pre-spawn HEAD as null both when the repository was unborn and when the probe failed. The registration path then skipped both cases, so amending the agent's own initial commit was blocked.\n\nCapture the pre-spawn state as either a SHA or an explicitly unborn HEAD. The promoted lineage check now requires a null predecessor only for the unborn case, while repository or probe failures still skip registration. Add a real-git regression for a promoted initial commit. Co-authored-by: Qwen-Coder <[email protected]> * fix(core): fail closed when the pre-spawn HEAD probe is terminated Only a genuine git non-zero answer (status 128, no signal) proceeds to the repository probe. A killed, timed-out, or failed-to-spawn HEAD lookup now returns null instead of asserting a verified unborn repository, which the promoted path could otherwise pair with a single-entry reflog to register a pre-existing root commit the agent never made. Co-authored-by: Qwen-Coder <[email protected]> * style(core): prettier formatting for probe regression test Co-authored-by: Qwen-Coder <[email protected]> * docs(core): scope the lineage guard's fail-closed claim to born HEAD R1-1: the docblock stated "a foreign commit never earns an exemption" as a fail-closed property, which the unborn arm makes false — `null` is a sentinel every root commit satisfies, so a root commit somebody else lands during the promoted window is indistinguishable from this command's own. Say that, and scope the five-star guarantee to a born HEAD. The behaviour is the trade tracked on #12523; only the written invariant was wrong. R1-2: pin that the PATH `git` shim is live before relying on it. Both closing assertions also hold when the shim is bypassed (the lineage check then rejects on a non-null predecessor), so the row could green without exercising the probe-termination path it exists to pin. Verified by mutation: changing the shim's match from `--verify` to `--bypass` turns this row red. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw1dkg4zbx * docs(core): scope the promoted-lineage claim to what the predecessor proves The `requirePredecessor` paragraph said "a foreign commit never earns an exemption", and the promoted call site said the guard keeps the registered SHA one this command created. The lineage check only proves the newest reflog `commit:` entry moved HEAD from `preHead`; a foreign commit landed directly on `preHead` while this command's own commit never landed has that same predecessor and still registers, so both statements overclaimed. Scope them: born-HEAD narrowing is not closing, and the fail-closed guarantee holds only once this command's own commit landed first. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw2g5c3hdf --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
2b94314b49 |
fix(cli): scrub advisor selector from startup errors (#16083)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
bbff05f035 |
fix(serve): treat a payload-invalid start reply as a definite refusal (#16460)
`HostedWorkspaceBroker.execute()` only treated the two 409 conflict codes as a definite "never started" refusal. When the Runtime Broker rejected a deferred tool payload it could not encode (an unpaired surrogate) it answered `:start` with HTTP 400 `runtime_payload_invalid`, which the client read as a lost reply: it polled the observation endpoint until `observationMs` (120s, up to 180s) and then surfaced the session as Hosted recovery-blocked with the workspace lease still held. A payload the Broker refused to parse never became an invocation, so no poll can turn it into a reply. Rethrow the 400 `runtime_payload_invalid` rejection from the `:start` catch alongside the 409 conflicts; the polling path and the unknown-execution reconciliation are unchanged. The new test drives a fixture Broker that answers `:start` with that 400 and asserts the rejection propagates with only the `:start` request observed; it fails before this change because the extra observation GET appears in the request path list. Patrol-Run: qwen-issue-patrol/jmw4xgcu3zh Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
3d9988abc2 |
fix(cli): surface stacked-skill overflow in non-interactive and ACP runs (#16404)
* fix(cli): surface stacked-skill overflow in non-interactive and ACP runs `parseStackedSlashCommands` already reports `exceededMax`, and the two interactive surfaces warn about it. The non-interactive path (`handleSlashCommand`) ignored the flag, so headless (`-p`, JSON, stream-JSON) and daemon/ACP runs silently dropped the extra `/skill` tokens with no signal at all. - carry the flag out of the stacked branch as `stackedSkillOverflow` on the `submit_prompt` result, using the same copy as the interactive surfaces; - headless writes it to stderr, so it never becomes a `message` of type `error` and the run keeps its exit code; - ACP emits it as a client-visible slash-command notice before the prompt runs. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-issue-patrol/jmw3ofzhmya * fix(channels): carry the stacked-skill overflow notice with the turn reply Channel bridges deliver only prompt()'s resolved text, so the overflow notice emitted on the slash-command channel was shadowed as soon as the turn produced model text and channel users never saw the warning. Tag the notice in `_meta` and route it into the turn's text in both bridges. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw3tg0yrfb * fix(channels): keep the turn notice when a boundary clears the chunks The notice was pushed into `chunks`, which `clearChunks` empties on every `responseBoundary`, and the `per_task` branch never read it at all, so the stacked-skill warning still disappeared on any turn that made a tool call. Give the notice its own buffer in both bridges that boundaries never clear, and compose it into every return path (turn text and `per_task` result). The slash-command slot keeps its existing precedence and the pinned response-boundary tests stay unchanged. Tests: tool-call-boundary and `per_task` arms for both bridges, plus a Session-side test that the producer tags the overflow notice with `turnNotice: true`; each new arm reds when the buffer is removed. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw3zvicvfk * test(channels): pin the turn notice on the real boundary and empty replies Address the follow-up review on the turn-notice carry: - R3-1: the daemon boundary test fired `responseBoundary` before the queued notice was consumed, so it cleared nothing; drive the boundary from an in-turn `tool_call` instead and assert it goes red when `clearChunks` regains `turnNotice = ''`. - R3-3: the `turnNotice` wire key was a bare literal on every side; export `CHANNEL_TURN_NOTICE_META_KEY` beside the sibling keys and use it in the producer and both bridges. - R3-4: a notice-only turn returned `"<notice>\n\n"`; drop the separator when the body is empty and cover the exact resolved string. - R3-5: cover the per-session filter in both bridges so another session's notice cannot prefix this turn's reply. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw445tvbfr * fix(channels): deliver skill overflow notices outside response buffers --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: zzj <[email protected]> |
||
|
|
e3cbea0096 |
fix(cli): surface workflow agent approvals in the interactive TUI (#16425) (#16432)
* fix(cli): surface workflow agent approvals in the interactive TUI (#16425)
A backgrounded workflow agent that hit a tool-approval gate deadlocked in
the interactive TUI: the registry parked the approval, the footer showed a
"needs approval" badge, but there was no way to answer it, so the run never
made progress. Two independent defects caused it.
1. `WorkflowRunRegistry.parkApproval` treated the display-only
`approvalChangeCallback` as a sufficient host channel. A host that could
only repaint the badge therefore accepted the approval and then had
nothing to answer it with. It now fails closed unless a real
`approvalRequestCallback` responder is registered, so a badge-only host
rejects the park and the caller sees a cancelled request instead of a
silent forever-wait.
2. `useBackgroundTaskView` never registered a responder at all, so even a
host that wanted to answer had no channel. It now registers its snapshot
refresh as the approval-request responder (and clears it on unmount),
which makes the parked approval part of `entries` — the surface the
background-tasks dialog renders and settles through
`resolvePendingApproval`.
`BackgroundTaskViewProvider` also promotes a parked workflow approval to
the detail view once per `approvalId`, because the approval can only be
answered from there; keying on the id keeps a user's "close" from bouncing
straight back into the dialog on the next refresh.
Scope: interactive TUI workflow approval path only. Headless and ACP hosts
are untouched.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmw4ig8e5z4
* fix(core): drop the redundant responder guard after the park gate
TS narrows approvalRequestCallback to non-undefined once the park gate
requires it, so the extra truthiness check is dead and fails tsc (TS2774).
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmw4ig8e5z4
* fix(cli): type the workflow approval fixture and format the new tests
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmw4ig8e5z4
* test(core): register a real approval responder in the orchestrator bridge test
The narrowed park gate rejects an approval when the registry has no
approvalRequestCallback, so a badge-only host (change callback only) leaves
pendingApprovals empty and the bridge test times out. Register the responder
the production host also provides, matching the gate's contract.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw4lb42xgf
* fix(cli): surface each parked approval once, in list mode (#16432 R1-1, R1-2)
The single-id latch keyed on the first pending entry's head approval, so a
second approval parked behind a still-parked head never surfaced. Latch a
set of approvalIds and select the entry holding an unsurfaced one.
Auto-open also landed in detail mode, which arms the tool confirmation
with focus while unmounting the composer: a stray Enter aimed at the
prompt approved the background command. Auto-open now lands in list mode
with the row selected; entering detail stays an explicit navigation.
Tests: flip the surfacing contract to list mode, add the masked-approval
regression (red on the single-id latch), drop a dead assignment (R1-8).
* fix(cli): disarm auto-opened task dialog and latch only the displayed approval head
An auto-opened background-task dialog unmounts the composer and keypress
dispatch is a broadcast, so a letter still aimed at the composer could
cancel (x) or pause (p) the selected workflow without confirmation, and a
stray Enter could arm the approval. Tag auto-opens and disarm x/p/Enter in
the dialog's own list branch until the first explicit navigation key
acknowledges the open; user-initiated opens keep immediate behavior.
The surfacing latch recorded the first unsurfaced approval, but the detail
view renders only pendingApprovals[0]; an approval parked behind a still-
parked head was marked surfaced without ever displaying and never prompted
once it became head. Latch only the displayable queue head so the next
approval prompts after the head drains.
Addresses the two Criticals from the #16432 human review.
* test(cli): pin the closed-dialog guard on the approval auto-surface effect
The surfacing effect's dialogMode guard had no test that failed without
it: every existing case fired refreshes only while the dialog was
closed. Add a case that opens the detail view, parks a workflow approval
mid-read, and asserts the view and selection stay put. Deleting the
guard turns exactly this case red (#16432 R2-3).
* fix(cli): hold the auto-open disarm until a real navigation key
- Acknowledge the auto-open only on arrow keys. The selection matchers also
carry bare k/j and Ctrl+P/Ctrl+N, and ordinary typed letters satisfy
them, so prose typed at the composer the dialog just unmounted released
the disarm and the next x/p/Enter reached the run (#16432 R2-1).
- Swallow closeDialog while the auto-open is unacknowledged: Escape, Left
and Ctrl+C burned the add-only latch before the prompt was ever seen and
the workflow fell back to the badge-only hang this PR removes. Ctrl+C
reaches the same path through closeAnyOpenDialog (#16432 R3-1).
- Unfocus the pill in the auto-open writer, matching openDialog, so the
pill's Enter cannot run openDialog() and clear the disarm (#16432 R3-2).
- Advertise only "arrows activate" while disarmed instead of Enter view /
x stop, which were swallowed (#16432 R3-3).
- Record the parked head ids at close so an acknowledged close stays
closed while another workflow is already parked, and a genuinely new
approval still surfaces (#16432 R3-5).
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw5b11gghf
* style(cli): wrap the over-width hint condition to the prettier limit
The R3 hint-row re-indent left the paused-agent condition one column
over the 80-char limit, failing the Lint & Static gate at
|
||
|
|
49ac6dae86 |
fix(sdk): name daemon UI tool blocks from _meta.toolLifecycle frames (#16306)
* fix(sdk): resolve tool name from _meta.toolLifecycle for web-shell labels Foreground subagent tool calls render as "unknown Tool" in the web-shell session view because the daemon normalizer's tool-name chain never reads `_meta.toolLifecycle.toolName`, the only name carrier on lifecycle frames emitted by transcript-replay. Such a frame projects a block with no toolName and the generic "Tool" title, and the web-shell adapter's `resolveToolCallName(...) || 'unknown'` renders that pair verbatim. Append the lifecycle name to the tail of the resolution chain so explicit `title` / `toolName` / `_meta.toolName` keep their precedence, and leave the silent-shell lifecycle discard guard untouched. Fixes #16301 Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-issue-patrol/jmw20fhpuwp * fix(sdk): name subagent tool blocks from lifecycle metadata The metadata-only guard dropped the one frame that carries a subagent tool call's name, so the previous fallback never ran and #16301's "unknown Tool" label survived. Forward a name-only tool.update from those frames and let the reducer name an existing block by toolCallId: it never creates a card and never replaces a human-readable title. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw23adg4cx * fix(sdk): close R2 review on lifecycle tool naming - Skip name-only tool.update events in the terminal renderer: they carry no status, so rendering one printed "tool undefined <name>" per tool call. - Return before the copy-on-write path when a nameOnly frame targets an already-named block, and keep retainedBytes accounting exact on rename. - Add the status-bearing lifecycle arm test and the nameOnly renderer test; both go red when their guard is removed. - Rewrite the comment above the lifecycle branch, which still claimed those frames are dropped. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw2opz8jds * fix(sdk): name mid-run attach tool blocks from the lifecycle frame R3-1: the name-only arm returned early when no block existed for the call id, so a client that attached mid-run (lifecycle frames before any create frame) still showed "unknown Tool". Park the late name per call id and seed the create path with it. R3-2: read the lifecycle fallback through extractToolLifecycle so a status-bearing frame makes the same v1 validation decision as the metadata-only arm. R3-3/R1-2: pin the create path with producer-shaped frames (no fabricated status), the no-overwrite guard, and the v2 rejection case. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw2zfs5be7 * test(sdk): pin the parked-name bound and name what the attach case pins Review R4-2: nothing pinned MAX_PENDING_TOOL_NAMES or its oldest-first eviction, so deleting the guard left the suite green. Add a case that parks 257 distinct call ids and asserts the map stops at 256, `call-0` is gone and `call-256` survives. Verified load-bearing: disabling the eviction guard fails it with 257 keys. Review R4-4: the "mid-run attach" case passes unchanged on the base tree, because its create fixture carries `title` and `_meta.toolName` — the pre-existing chain already reads those, so the parked name is consumed and discarded. Rename it to what it actually pins; the sibling case `seeds the late name when the status frame carries none` is the one that discriminates the parking path. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw3p5pfkf6 --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
8660f75430 |
fix(ci): refresh host assets on older PR checkouts (#16476)
* fix(ci): refresh host assets on older PR checkouts Co-authored-by: Qwen-Coder <[email protected]> * style(ci): format host asset regression cases Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
cb282ef5cf |
test(web-shell): unit-test settings development route (#16456)
Replace the full Vite dev-server startup with a direct middleware test so the settings route contract no longer depends on Babel and App.tsx cold-start time. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
0964c4bc3c |
test(serve): cover the workspace_runtime_stop boot-time conjunction (#16469)
The stop surface is advertised only when five boot-time legs hold: managedChildProcesses, workspaceRuntimeRemoval, a live ACP http handle, manageScheduledTaskSessions, and a managed runtime whose bridge exposes stopWorkspaceRuntime/getRuntimeStopCompletion/getRuntimeStopSnapshot. Nothing exercised that computation from /capabilities: the only existing references inject the workspaceRuntimeStopAvailable toggle directly, so any single leg could regress while the suite stayed green. The new test mounts the real app with every leg wired and asserts the feature is advertised, then drops one required leg and asserts it is not. Part of #12563, does not close it. Patrol-Run: qwen-issue-patrol/jmw51qoerzl Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e61ad3959e |
fix(managed-agent): settle a title the dispatcher claimed first (#16447)
* fix(managed-agent): settle a title the dispatcher claimed first renameSession admits the RENAME row and then claims it. The background SessionRenameCoordinator scans rows a request has just admitted, so its claim can win without any concurrent caller, and the request answered 503 "The Session title is already being delivered." for its own rename. Wait a bounded 2s for that delivery to settle and answer the recorded command instead of failing the request. Fixes #16431 Co-authored-by: Qwen-Coder <[email protected]> * fix(managed-agent): keep the creator gate on the rename settle arm The settle arm answered the recorded rename outcome without re-running the replay actor gate, although it shares the COMPLETED read-back's TOCTOU window: a pre-read that missed the recorder's row lets a non-creator OPERATOR reach the settle through the duplicate-key winner path and take a 200 the COMPLETED arm refuses with 409. Mirror that arm's gates — requireReplayActor on the settled Session, and the last-visible serialization — so both arms answer identically. Test: a non-creator OPERATOR racing the recorder's key through the settle arm meets the same 409 workspace_unavailable as through the COMPLETED arm, while the recorder still replays its own outcome; the dispatcher's completion is asserted so the refusal provably comes from the settle arm. Load-bearing: reverting the gate answers the raced request 200 and fails the test. --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
989d31ee9e |
test(managed-agent): retarget the lifecycle IT and retry snapshot claim deadlocks (#16446)
The publication-lane IT still parked the receipt thread on the placement guard's `INSERT INTO ma_tenant_guard`, but #16354 moved ordinary publication lanes onto the Session root, so that statement never runs and the test now fails on its own latch instead of on a product lock cycle. Park the receipt on the root statement its lane actually executes. The sibling native-snapshot IT exposed a real gap: two publishers of the same snapshot claim the manifest row with `INSERT ... ON DUPLICATE KEY`, and InnoDB can resolve that by rolling one side back as a deadlock victim. Retry the claim a bounded number of times so the loser reads the winner's row and answers the replay descriptor instead of surfacing a deadlock. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
1ecf444010 |
test(interactive): poll the foreground-shell frame instead of snapshotting it (#16455)
#15712 recurred on 2026-11-05 (run 42069115052, macOS): the case asserted `compact(frame('mode-before'))` contains `YOLO mode` straight after a PTY write, and on a loaded host the approval footer had not repainted yet — the captured frame still held the startup banner, so the assertion failed on a correct UI. The draft snapshot just above it has the same shape. Poll both through the suite's `until`, which already scales its budget 3x under CI (#16276), so a slow repaint is waited out while a genuinely missing footer still fails on the same message, with the frame kept in FRAMES.json. Verified locally: the case passes (16.9s) and the whole file passes (3 passed, 1 skipped). |
||
|
|
1a20792cfa |
ci: route the no-AK integration gate to ECS for every pull request (#16132)
* ci: route the no-AK integration gate to ECS for every pull request
Same trust class as lint_and_static (2026-11-02 extension of the
2026-10-31 decision): the integration step executes PR code, but the
lane carries zero secrets by construction — every model key is emptied
at the step and the job's only token is the read-only checkout grant.
The gate's step-level runner.temp HOME becomes run-unique and is
cleared before use (the R2-1 lesson: RUNNER_TEMP survives across jobs
on this pool). test and integration_cli keep the association-gated
ubuntu_runner. Mutation-verified: runs-on revert, fixed HOME, and a
real key in the PR-code step each red the routing battery.
* fix(ci): isolate no-AK HOME from the first step; value-pinned zero-secrets
Round-2 review findings: (1) Critical — the gate-scoped HOME missed the
lane's actual fork-code start: checkout's local action and the install
(lifecycle scripts plus scripts/prepare.js, un-skipped) ran under the
runner's real shared home with a shared COREPACK_HOME — the exact
cross-job poisoning channel the lint acceptance closed. Mirror lint:
first-step GITHUB_ENV run-unique HOME + COREPACK_HOME inside it +
shared verified XDG cache + always() reclamation. (2) Critical — the
zero-secrets guard was name-based and missed odd-named secrets; enforce
by VALUE (every non-path env key empty, no secrets.* anywhere in the
gate). (3) runs-on literal pin tightened (timeout line also mentions
lint_runner). Docs: EN single-lane phrasing, stale merge-queue claim,
2026-11-02 paragraph rewritten for the first-step shape. Six mutations
red (first-step removal, shared corepack, odd-name secret, no cleanup,
runs-on revert, fixed HOME).
* docs(ci): correct the enforcement-order claim and stale trust-basis note
Round-2 review: the doc claimed the routing battery runs before
dependency install — false (HELPER_TESTS imports yaml, runs after
install, and not at all on github_ci_only PRs' own runs); and the
ci.yml trust-basis comment still described the round-1 gate-scoped
HOME shape. State the actual timing and detection latency, and the
shipped first-step shape.
* fix(ci): scrub ANTHROPIC_AUTH_TOKEN at the no-AK gate; honest cache naming
Round-3 review: the Anthropic SDK's auth resolver back-fills
ANTHROPIC_AUTH_TOKEN from env, so a runner-account residue would survive
into fork PR code on the pool — add it to the gate's scrub list (pinned
by both suites). The 'Configure persistent npm cache' step name promised
persistence the per-run HOME removes; rename it and record the accepted
cold-install cost (shared pnpm store stays a follow-up).
* fix(ci): scrub ANTHROPIC_AUTH_TOKEN at the no-AK gate; honest cache naming
Round-3 review: the Anthropic SDK's auth resolver back-fills
ANTHROPIC_AUTH_TOKEN from env, so a runner-account residue would survive
into fork PR code on the pool — add it to the gate's scrub list. The
value whitelist only iterates present keys, so the deletion mutation
escaped it; pin the key's presence-and-empty in the battery and the
28-key literal list. The 'Configure persistent npm cache' step name
promised persistence the per-run HOME removes; rename and record the
accepted cold-install cost (shared pnpm store stays a follow-up).
* test(ci): pin the no-AK grant, HOME template and reclamation path
Three assertions in the no-AK routing battery proved less than they read:
- the job's `permissions` was never inspected, so the read-only-checkout
grant that justifies routing untrusted fork code onto the pool could be
widened without a red test;
- `create.includes('HOME=')` is satisfied by `COREPACK_HOME=` and
`XDG_CACHE_HOME=`, so deleting the real HOME/USERPROFILE publication kept
the test green;
- the cleanup only had to contain the `qwen-no-ak-home-` prefix, so a path
that dropped `-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}` would still pass
while removing a directory nothing used.
Pin the grant by value, the publication by its printf template, and the
reclamation by the exact run-unique path the create step builds.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw48g5gafx
* test(ci): pin the realized HOME values in the shared ungated lane loop
R2-1: assert the create `home=` line, the gate QWEN_HOME and the cleanup
command by value, and require the exact set of `$GITHUB_ENV` writers. An
`echo` stand-in for the cleanup, a `-v2` create path and an appended second
HOME writer all kept the battery green under containment checks.
R1-2 / R1-1: hoist those pins into the shared `ungated` loop so
`lint_and_static` gets them too. Its create step was pinned by
`includes('HOME=')` (satisfied by `COREPACK_HOME=`) and its cleanup by
`includes('qwen-')`, so a dropped `HOME=%s` or a retargeted `rm -rf` survived.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw4h0sl6g9
* test(ci): red on later republishes of the isolated HOME keys
R3-1: the GITHUB_ENV equality guard read step 0 only, so a later step that
republished HOME reverted the run-unique home by last-write-wins while every
assertion stayed green — the isolation would silently stop applying to the
steps that run PR code. Scan the remaining steps for isolated-key
$GITHUB_ENV writers and for step-level HOME/USERPROFILE/COREPACK_HOME/
XDG_CACHE_HOME overrides, leaving the npm cache step's own key a legitimate
later write.
Mutation-verified on this head: appending printf 'HOME=%s\n' ... to
integration_no_ak's 'Configure per-run npm cache (self-hosted)' step and
adding a step-level `env: HOME` each turn the suite red (103 pass -> 102
pass / 1 fail); reverting restores 103/103.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmw4ng9wvgi
---------
Co-authored-by: Qwen-Coder <[email protected]>
|
||
|
|
2666e3552d |
fix(core): make persisted session pagination safe for equal mtimes (#16024)
* fix(core): make persisted session pagination safe for equal mtimes
listSessions() ordered files by mtime alone and resumed the next page with a
strict `mtime < cursor`. An mtime is not unique — bulk copies, backup restores
and coarse-timestamp filesystems all produce ties — so a group of files sharing
an mtime that straddled a page boundary was dropped while hasMore still reported
progress, then the next page came back empty: 25 files at one mtime with size 5
showed 5 items in total.
Sort on (mtime desc, name asc) so the order is total, and let the cursor carry
that position: nextCursor is now `"<mtime>|<name>"`. A bare number is still
accepted with its existing strict `<` meaning, so callers holding a legacy
cursor keep working, and the daemon's opaque string cursor now passes the
composite form through instead of rejecting it as invalid.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmvwpo7jxrk
* fix(cli): carry the composite session cursor type through callers
listSessions() now hands back a "<mtime>|<name>" cursor so a page boundary
inside a group of equal mtimes does not drop sessions. The widened
`number | string` contract left the CLI callers that store or compare that
cursor typed as `number`, which is a type error in `tsc --build` even though
nothing changes at runtime.
Widen the three consumers to `number | string`: the daemon's persisted scan
cursor, the live coordinator's paging plus its loop guard, and the session
picker state used by both the ink and opentui pickers. No behaviour change.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmvwrtdc1rm
* fix(cli): route composite session cursors to the persisted listing
The persisted listing mints `"<mtime>|<name>"` cursors when a page ends
inside a group of files sharing an mtime, but the route still chose between
the persisted and the live listing path with a numeric-only predicate. A
composite cursor therefore fell through to the live path, whose parser only
accepts numbers, and the second page answered 400.
Export one predicate from session-list.ts carrying the same shape test the
cursor parser already applies, and use it for that routing decision.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmvwtyj3bro
* fix(session-list): keep composite cursors usable by ACP and VS Code clients
listSessions mints "<mtime>|<name>" cursors whenever a page ends inside a
group of files sharing an mtime, but two consumers still parsed every cursor
as a number: ACP session/list rejected page 2 with "not a valid numeric
cursor", and the VS Code companion turned the composite cursor into NaN so
hasMore read false and only page 1 was ever listed. A malformed "|name"
cursor also slipped through the persisted-cursor classifier and answered 200
with an empty page instead of invalid_cursor.
- acpAgent: accept and forward "<mtime>|<name>" cursors to listSessions
- session-list: require a non-empty mtime prefix before classifying a
cursor as persisted
- vscode companion: pass the daemon cursor back verbatim, widen the cursor
type and treat only a non-empty cursor as "append to the current page"
Tests: ACP composite passthrough + empty-prefix rejection, VS Code
getSessionListPaged composite nextCursor round trip.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvxke6bm6i
* fix(vscode): keep composite session cursors type-safe in the companion
|
||
|
|
45f71b3714 |
fix(ci): apply the apt-mirror probe to the release docker lane too (#16278)
* fix(ci): apply the apt-mirror probe to the release docker lane too The release docker integration lane builds the same sandbox image as the E2E leg but never selected an apt mirror, and it prunes the builder cache on every run so its apt layers are always cold. On mainland pool hosts that rebuilt against the throttled deb.debian.org path (11-45 kB/s) — the same defect #16214 fixed for E2E, left unfixed here because it was out of scope (#16271). Extract the probe (apt_mirror_probe_rate / select_apt_mirror) and a new apply_apt_mirror helper into a shared .github/scripts/apt-mirror-probe.sh, sourced by both run-e2e-tests.sh and run-release-docker-integration.sh. Each calls apply_apt_mirror before build:sandbox, so a mainland host builds against the VPC intranet mirror (6-9 MB/s, verified for E2E) while a healthy default is left untouched. The Dockerfile APT_MIRROR ARG already exists from #16218. Contract tests read the shared probe and pin the candidate loop and the source statement (not substrings the comments also satisfy); both callers must apply the probe before the build. Mutation-verified: removing the release call, a candidate, or a source line each reddens the suite. * fix(ci): carry the apt-mirror probe into the shard-retry test rig run-e2e-tests.sh now sources its sibling apt-mirror-probe.sh at startup. The shard-retry meta-test copies only run-e2e-tests.sh into a temp dir, so the source resolved to a missing file and 'set -e' aborted before any shard ran — all eight cases saw zero npm calls. Copy the probe file alongside the script in the rig so the source resolves; sourcing only defines functions (no network at source time) and the sandbox:none path never calls apply_apt_mirror, so the rig stays hermetic. Witness: CI at |
||
|
|
7c6f93ca51 |
fix(cli): forward the folder-trust store into the container sandbox (#16390)
* fix(cli): forward the trusted-folders store into the sandbox The ACP hook tests red only in the docker lane since the folder-trust feature was enabled in their child home (#16264): the rig grants the workspace through QWEN_CODE_TRUSTED_FOLDERS_PATH, but start_sandbox never passed that path through. The container fell back to <QWEN_HOME>/trustedFolders.json, found no trust decision, and the settings layer dropped the workspace scope, so the approved project hooks never reached the registry. Forward the override like the MCP and hook approvals paths, translating the Windows path first. Co-authored-by: Qwen-Coder <[email protected]> * test(serve): allow the trust-store read in the sandbox env guard The sandbox launcher now reads QWEN_CODE_TRUSTED_FOLDERS_PATH twice while forwarding it, and the serve process.env guard pins every access. Register the new read in the launcher's allowance so the guard suite stays green. Co-authored-by: Qwen-Coder <[email protected]> * test(serve): pin unset sandbox env stores Cover every forwarded approval/trust store in the unset branch, so dropping a guard cannot silently pass "undefined" into the container. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
c8eaa04e81 |
test(review): pin the carried-ledger carrier order and fail-open arms (#16395)
Four test-only gaps in dedup-candidates.test.ts; no production change: - a claim both carriers hold is dropped once and cites the POSTED carrier, not the artifact's mirror of it (carried is posted-then- deferred), with both halves asserted loaded so the order is what decides the citation - the side-file read fails open on its own two arms, which the absent- file case does not reach: a truncated JSON file lands in the parse catch, and a non-array `findings` / `round: 0` file is refused by the shape guard — no throw, no drop either way, each with a control that the same claim DOES drop when the file is intact - a kept candidate reaches kept[0] with every extra field verbatim, so the Step 4 verify shards can still be built from the kept list - the deferral half's three keep arms (far anchor, loose overlap, Critical candidate vs non-Critical entry) each keep, with a control that the same pair still absorbs under matching input Every new case was verified to have teeth: each was pushed red against a mutated production behaviour (carrier order swapped, parse catch rethrowing, each shape-guard leg removed alone, kept narrowed, anchor tolerance widened, similarity bar removed, severity guard removed) and the production file was restored byte-identical afterwards. Patrol-Run: qwen-issue-patrol/jmw3k5nxoy7 Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
b87d60ddf0 |
fix(serve): serve qualified hooks status for untrusted workspaces (#16388)
* fix(serve): serve qualified hooks status for untrusted workspaces The workspace-qualified hooks status GET sat behind the trusted-runtime gate alongside env/preflight, so with folder trust enabled an untrusted workspace got 403 exactly when the Web Shell approval card needed the pending decision. The card treats the failed read as no hooks and never renders, leaving no interactive way to approve. Route the hooks read through the same trust-free resolver as the qualified approve POST; env/preflight keep the trusted gate. Fixes #16387 Co-authored-by: Qwen-Coder <[email protected]> * test(serve): pin pending hooks decision for untrusted workspace Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4e75789d8e |
test(web-shell): pin the rewind capacity-latch re-open (#16358)
* fix(web-shell): defer rewind capacity-latch re-open publish past the rebuild commit (#16347) A rewind drops the newest blocks, and the rewind rebuild that follows replays the surviving prefix synchronously. Publishing the capacity-latch re-open synchronously inside the truncation callback let a concurrently batched same-anchor page append land before the rebuild's prefix, which macOS CI observed once as a block-order inversion in DaemonSessionProvider's 'reopens a rejected history page after authoritative rewind with same anchor'. Keep the latch ref updates synchronous (loadMore gating and synchronous readers still see the reopened latch immediately) and defer only the React state publish one microtask, past the rebuild commit. Oldest-first trims keep the fully synchronous path. Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): pin the rewind capacity-latch re-open and correct the fix comment Addresses two review findings on the #16347 fix: - The deferral was unpinned: no test could distinguish the deferred publish from the previous synchronous one. Add a case that latches the capacity rejection, commits a rewind rebuild, and asserts the latch re-opens once the rebuild frees the window. A regression that drops the truncation reconciliation on the rebuild's store.reset() turns this red while the pre-rewind latch assertions stay green. The deferred-vs-synchronous distinction itself is not observable through React act() microtask flushing, so the test documents that it pins the re-open contract, not the queueMicrotask mechanism. - The comment described the rebuild as following the publish; the rewind reducer installs the surviving replay prefix BEFORE invoking the truncation callback. Restate the ordering against the store commit and the notify microtask. Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): pin the rewind capacity-latch re-open Drop the one-microtask deferral of the rewind latch publish: the truncation callback runs from the SDK reducer after state.blocks is sliced, and the publish carries only latch metadata, so a microtask cannot reorder any store-level step. The added test stays - it pins that a rewind rebuild re-opens the capacity latch and the rebuilt window keeps only the surviving prefix. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw3caqrueo --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
c0782a8cb6 |
test(ci): pin the zero-skip timeout shape in awaitTwoSkipLines (#16384)
Refs #10737 Patrol-Run: qwen-issue-patrol/jmw3fvcf8y3 Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
6fe5bca8e1 |
test(managed-agent): match the busy-storage message in ChildWorkspaceServiceTest (#16382)
`WorkspaceExecutionStore.busy()` changed its message to "Workspace is not available for this operation." in #16369, which updated the in-test stub at line 1345 but not the `aBusyStorageParksTheStepWithoutSpendingAnAttempt` assertion at line 707. The assertion still expected the old "held by another tool turn" text, so the test now fails deterministically on the new message (seen on run 41981102231, both attempts). Update the assertion to the new busy message. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
feb06ca823 |
test(managed-agent): declare v2 reader in cleanup-conflict MySql IT (#16372)
`HostedCleanupRetentionMySqlIT.cleanupAdmissionConflictIsReturnedOverHttpOnMySql` expects 409 `managed_session_cleanup_conflict`, but its commit carries a `hosted.cleanup` event, which `ManagedHostedRecoveryRecords.requiresVersionTwo` classifies as requiring storage v2. `JournalCommit` then calls `readerVersion.requireReadable(2)` before the cleanup-conflict check, and the request never set the `X-Qwen-Managed-Max-Readable-Storage-Version` header, so `ManagedSessionReaderVersion` defaulted the ceiling to 1 and threw `managed_session_reader_version_unsupported` first. The 2026-11-04 nightly failed on exactly that mismatch. Declare the v2 reader ceiling (the same header `ManagedSessionStoreIntegrationTest` uses when it expects the commit to be accepted) so the request reaches the cleanup-admission path the test actually exercises. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
406258d66a |
fix(core): headless project-hook trust requires the folder-trust feature (#16264)
* fix(core): headless hook trust requires the folder-trust feature The reporter PoC runs headless (--output-format stream-json with a one-shot prompt), so the unconditional headless exemption left the reported execution path open on a fresh default environment. Require the operator to have enabled security.folderTrust.enabled before the workspace choice counts as implicit trust; with the feature off, project hooks are withheld in every mode. Embedders enable the feature in their managed home settings and keep working via their trust store. Verified locally against the reporter scenario: default fresh home -> marker absent; feature on + trust-store grant -> marker written. Refs #14746 Co-authored-by: Qwen-Coder <[email protected]> * refactor(core): unify the project-hook gate on explicit trust The headless exemption branch never changed an observable outcome: the CLI settings layer already drops workspace settings for an undecided folder once the feature is enabled, and with the feature off the explicit predicate is false anyway. Replace both branches with the single explicit-trust rule so interactive and non-interactive callers share one contract. Behavior is unchanged; verified against the reporter PoC (default fresh home withheld, trusted path executes). Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): address review findings on the unified hook trust gate R1-1: the headless feature-off test wrote folderTrust into workspace settings, where WORKSPACE_RESTRICTED_SETTINGS strips it — the rig user scope stayed enabled and the test never reached the named gate. Write the feature-off decision into the user-scope home file instead. R1-2: the docs sentence overclaimed; IDE-granted trust loads hooks with the feature off. State the predicate as shipped. R1-3: headless withholding was silent for the newly gated population. Emit one stderr line from the non-interactive entry and a debug-log warn from the registry when the trust gate is why the scope is absent (getTrustWithheldProjectHooks, diagnostics only). Verified: reporter PoC withheld with the stderr line present; core 1052 passed; tool-hook integration 14 passed. Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): surface trust-withheld hooks in the workspace overview The hooks listing already marks why a project hook is disabled, but the ACP status serializer dropped the reason, so the web-shell overview only showed a count while trust-withheld hooks silently did not run. Carry disabledReason through ServeHookEntry/DaemonHookEntry (optional, older daemons unaffected), count untrusted hooks in the summary, flag the facet, and name the remedy in the tooltip (en + zh). Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): pin the trust-withheld hooks tooltip wording Component-level witness for the untrusted-hooks facet: the row shows the withheld count and the title names the remedy. The sidebar hooks facet is opt-in for embedding hosts, so the jsdom suite is the reachability-independent pin. Co-authored-by: Qwen-Coder <[email protected]> * test(cli): align fixtures with the surfaced hook state The workspace-hooks status test now expects the disabledReason the serializer carries, and the headless-warnings config stub provides the new getTrustWithheldProjectHooks accessor. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): tolerate partial config stubs in the headless hook notice The resumed-transcript and stream-json housekeeping tests use minimal config stubs; optional-chain the two accessors the notice reads, and provide the accessor on the remaining isInteractive stubs. All 124 llm.test.tsx cases pass. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): make the withheld-hooks surfaces reachable and equivalent Review findings on the status surfaces: the listing never carried disabledReason untrusted for settings project hooks (the trust gate keeps them out of the registry, and only agent/session rows were trustGated), so the web-shell hint was unreachable; buildHooksListing now appends the diagnostics block as disabled (untrusted) rows, with a real-listing test. The headless stderr notice now mirrors the TUI predicate (raw workspace hooks non-empty, exclude disableAllHooks and safe/bare), covering the feature-on undecided case the Config accessor cannot see. The facet value stays language-neutral; the localized withheld count and remedy live in the tooltip. Core 1035, llm 124, web-shell sidebar 51 tests pass. Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): browser witness for the withheld-hooks status surface Real Chromium e2e: open Daemon Status via the rail More popover, switch to Diagnostics, and assert the workspace.hooks section renders with the withheld count from a report shaped like the real daemon output. Screenshot retained as test output. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): fold the deferred approval follow-ups into this PR Per review direction, land the #16295 items here instead of a follow-up: - Explicit projectHooksApproved state: a stored approval keeps execution authority inside a command-line --yolo session (previously the trust gate withheld it because the pending flag is undefined while exempt). The flag never doubles as nothing-to-approve; mid-session decisions update it. Tests pin yolo+approved executes, yolo+unapproved withholds. - Display-only raw workspace block: with the feature on and the folder untrusted the settings layer drops the workspace scope, so the listing could not show the withheld hooks. projectHooksDisplayOnly feeds only getTrustWithheldProjectHooks, never the registry; test pins both halves. - Variable-source wording: the dialog hint now says environment (project .env/settings.env or shell), not only the approved project env. Per-hook approval granularity stays out: it is a feature iteration, not a correctness fix. Core 1017+1053, cli config 565, approvals 50, llm 127 pass. Co-authored-by: Qwen-Coder <[email protected]> * style: prettier the reworded dialog hint Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): /hooks list shows withheld project hooks in ACP/web-shell The non-interactive /hooks list read only the registry, so an ACP session (web-shell) with unapproved project hooks answered \"No hooks configured\" — the user could not see what was withheld or why. The list now includes the approval/trust diagnostics blocks, marked disabled (not approved / rejected / folder not trusted). Verified against a real ACP session: the withheld SessionStart hook is listed with its reason. Tests pin all three reasons. Co-authored-by: Qwen-Coder <[email protected]> * feat(web-shell): explicit project-hooks approval prompt for withheld hooks Web Shell previously had no interactive surface for the project-hooks approval gate: users had to run /hooks manually. The daemon now reports the pending decision (with sanitized hook lines) on /workspace/hooks, including on a cold daemon and when folder trust withholds the workspace scope, and a dedicated POST /workspace/hooks/approve route (plus the workspace-qualified twin) records the same hash-bound decision the TUI dialog writes. The Web Shell renders an Approve / Keep-disabled banner from that state. This is deliberately not session/request_permission: automation clients auto-answer permission prompts, a trust decision must be an explicit user act, and approving never grants folder trust. Co-authored-by: Qwen-Coder <[email protected]> * refactor(web-shell): render hooks approval with the shared ToolApproval card Replace the custom full-width alert banner with the existing ToolApproval card in the standard approval slot above the composer, so the hooks trust decision looks and behaves like every other approval: same panel, option list, keyboard handling, and safe-default focus. The card supplies a project_hooks request with decision-specific option labels; ToolApproval localizes its heading and question for that tool name. Co-authored-by: Qwen-Coder <[email protected]> * fix(web-shell): hooks approval probe reads once and polls only while pending The approval card polled /workspace/hooks unconditionally, adding background metadata reads the workspace-overview quiet-period contract forbids. Read once per mount/workspace/settings change, poll only while a decision is pending, and let the E2E quiet-period test settle the initial probe before counting sidebar traffic. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): apply the persisted decision once and to every live session Local review + ponytail over the PR diff: - The ACP child no longer re-derives and re-writes the approval hash on the approve control. The daemon persists the decision for the exact block the user reviewed; a second write could bind it to a block that changed since the review. The child only applies the decision, to every live session config, not just the bootstrap one. - The raw-workspace review subject hashes the project env files regardless of current folder trust, so a decision recorded while untrusted still matches after the folder is trusted. - The headless trust warning no longer doubles with the approval-gate warning for a pending block; the registry debug-log duplicate and its interface member are removed (the stderr line and listings already report the withheld state). - Drop dead test stubs for an accessor llm.tsx never reads, a redundant type member, and a stray blank-line deletion. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): scope the approve decision to the admission-bound workspace The per-session fan-out matched every live config in the child. Key it on the admission-bound workspace root (the same root the settings handlers use) against the request's cwd instead: session/cd moves the live target dir, and a decision recorded for one workspace must not clear or set the gate on a session admitted under another. Regression test covers same-workspace, cd-moved, and foreign-workspace configs. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): bind the project-hooks approval to the reviewed block The daemon's approve route re-read .qwen/settings.json at click time and persisted the hash of that fresh read, while the ACP child applied the decision to the block it rendered at startup. A file change inside that window (git pull, another session, the workspace's own agent) recorded an approval for hooks the user never saw, and left the card pending because the stored hash matched neither side. The child now persists against the same read that produced the status cell it rendered, so the reviewed block and the recorded block are the same object. The daemon only falls back to its own read when no child answered, which is the cold-daemon case where that read is what the client saw. Also unbreaks the web-shell lane: App.test.tsx's daemon-react-sdk mock had no useOptionalWorkspace export, so every App render threw and 1320 tests failed; and the workspace-overview facet counts no longer include the per-mount /hooks probe, which broke the exact facet-set assertion. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw2kfno0dl * fix(cli): explicit folder trust alone runs project hooks The startup gate demanded a stored approval even when the folder was explicitly trusted, contradicting the documented either-or authority: core's readProjectHooks accepts approval OR explicit trust, but the pending flag short-circuited the accessor before trust was consulted. A platform that pre-trusts its managed directories (folderTrust enabled plus a TRUST_FOLDER/TRUST_PARENT entry) saw every project hook withheld headless. Clear the pending gate for a trusted folder unless the exact block was explicitly rejected — a recorded 'no' still wins over trust. Verified headless on a real fixture: fresh home withholds; trust config alone fires the hook; a rejected block stays withheld in the same trusted folder. Gate unit tests cover pending/rejected/approved under explicit trust. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): bind the approval decision to the rendered block hash end to end The status cell now publishes pendingApprovalHash, the Web Shell card echoes it with the decision, and both the daemon route and the ACP child verify it: the daemon rejects a decision whose hash names a block other than the current file (409 project_hooks_changed, nothing persisted), and the child applies a hash-bound decision only to configs holding that exact startup subject (projectHooksApprovalSubject), leaving any config with a different block pending. Closes the cold-daemon re-read window: an approval can no longer bind commands the user never saw. Verified on a real daemon: render hash -> edit the hook file -> POST with the rendered hash returns 409 and persists nothing; re-render -> approve with the fresh hash persists and enables the hook; a real browser session then fires it. Card tests 5/5 (including hash echo), approve-control tests 2/2, gate tests 20/20, build + bundle clean. Co-authored-by: Qwen-Coder <[email protected]> * fix(hooks): close the remaining approval-gate review findings - Escape on the Web Shell trust card defers without recording a decision (TUI semantics): ToolApproval gains escapeDismisses/onDismiss, and the card hides until the block hash changes instead of persisting a rejection for a habitual keypress. - The card's options are *_once kinds so the shared safe-default logic pre-selects the reject side; a stray Enter on the security gate can never grant hook execution. Decision-specific labels stay via the option-id i18n keys. - The hooks-trust card reports into approvalOverlayActive, so the composer is dormant while the card owns the keyboard, exactly like a tool approval. - Under command-line YOLO, replacing the hooks block drops the old approval authority: an edited block cannot execute on the previous block's approval while the gate is exempt. - The trust-withheld listing surfaces the display-only block even when the empty execution block reads the gate as cleared (feature on, folder untrusted) — the state the copy exists for. Tests: core config + hooks-listing 1055/1055, hooks reload 38/38, ToolApproval + card 99/99 including Escape-defer and reject-default regressions; build + bundle clean. Co-authored-by: Qwen-Coder <[email protected]> * chore: record merge provenance The main merge ( |
||
|
|
2800ea18da |
fix(cli): keep project hook approval env inside the workspace (#16316)
* fix(cli): keep project hook approval env inside the workspace
Approved project hooks were silently withheld in the docker sandbox lane.
The workspace trust walk admits an ancestor `.env`, the host hashed it into
`hookApprovals.json`, and the container — which mounts only the workspace —
recomputed the hash without it, so `getState()` returned `pending`.
Narrow `readProjectEnvFiles` to files under the workspace root. `findEnvFiles`
still discovers ancestors for the runtime environment, so only the approval
hash input changes. Hooks in a workspace with no ancestor `.env` keep their
current record, but every hash that covered an ancestor changes once, so those
approvals reset to pending a single time.
Fixes #16313
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmw24pta9wt
* style(cli): satisfy prettier in environment hook-approval test
The incremental Prettier lane selected packages/cli/src/config/environment.test.ts
and rejected the single-line hashProjectHooks call added in
|
||
|
|
2b9c4a98f4 |
fix(ci): pin COREPACK_HOME for Linux test shards on cold-HOME runs (#16328)
The Linux unit-test shards set HOME to a fresh per-run directory for isolation. scripts/tests/corepack-warmup.js (vitest globalSetup) spawns `corepack pnpm --version` before any worker forks, and on a cold HOME corepack downloads the pinned pnpm from registry.npmjs.org. The ECS egress intermittently times out on that fetch (ETIMEDOUT to 104.16.9.34), which kills the shard after every test file has already passed — today that produced 7 PR-facing failures across random shard numbers (2/8, 5/8, 3/8, etc.), all on the same self-hosted host. Install dependencies runs before the test step and warms the corepack cache under the runner's real HOME, but the test step's HOME override makes that cache invisible. Pin COREPACK_HOME to a per-run path in both steps so they share the same cache without touching the HOME isolation. Per-run, not persistent: corepack does not re-verify extracted entries on cache hit, so a shared cache would be a poisoning channel (the same reason lint_and_static keeps its COREPACK_HOME per-run). Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
ed6760aa2c |
ci: reclaim qwen-autofix workflow headroom (#16069)
* ci: reclaim qwen-autofix workflow headroom Co-authored-by: Qwen-Coder <[email protected]> * ci: keep the qwen-autofix anchors the shape pins slice on The headroom pass collapsed three comment blocks and merged two `Full rationale` pointer lines. The workflow-shape tests use those exact comments as slice boundaries for the real bash they execute, and the pointer parser reads only the first `qwen-autofix.md#af-NNN` per line, so four pins went red on Test (ubuntu). Restore the `# Convergence-break mirror` and `# Convergence-signal circuit breaker (#10107): the park action on` wording, and let every pointer id spell its own anchor. No behavior change; refresh .size-baseline and the doc byte count to the new file size. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvyc99g07m * docs(ci): restore the rationale the autofix compression dropped R1-1: af-085 answered only about the allowlist, so the worktree-config file, its pairing with extensions.worktreeConfig, and the hooks-path resolution ordering read as dead code next to a KEPT allowlist entry. Restore both paragraphs in the anchor the yml already points at. R1-6: the age-sweep comment pointed at af-149, which documents heartbeat and pid liveness only, while the reclaim reason lived nowhere. State it at both sweep sites. R1-3: the design record hardcoded the workflow's byte count, which the ratchet can invalidate without reddening anything. Name .size-baseline as the single source of the number instead. The bytes R1-6 adds land in .size-baseline (451750), so the recorded size still equals `wc -c .github/workflows/qwen-autofix.yml`. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw0wf5wvb8 * fix(ci): repoint two hollow rationale pointers and restore two rationales Address the four blocking hollow-pointer findings on the autofix size extraction (each verified by extracting the anchor span at this head): - gh-config mint (~4980): af-112 documents the git preamble, not the gh pins; repoint to af-149, which carries the GH_HOST/hermetic_gh material. - heartbeat rm -rf (~3742): af-149 lacks the '-rf, not -f' doctrine; repoint to af-158, which states it verbatim. - eval-marker parse (~2756): restore the POSITIONAL field-order rationale (.[0]=ts .[2]=round .[3]=win .[4]=run; every write site emits that order) into af-159. - WORKDIR cleanup (~1584): restore the 'last step, after every reader including the artifact upload' ordering rule into af-148. The two repoints are same-length, so qwen-autofix.yml stays 451750 bytes (.size-baseline unchanged). Workflow meta-test suite 350/350 pass. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
a5fec03684 |
fix(ci): fail Java lanes before disk exhaustion (#16068)
* fix(ci): fail Java lanes before disk exhaustion Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): gate the MySQL pool lane instead of a hosted job The disk floor gate landed in hosted-harness-mysql, which runs on ubuntu-latest, so `runner.environment == 'self-hosted'` is never true there and the step was dead code. The job that actually shares the ECS pool disk - mysql-integration, two Maven suites plus a per-leg MySQL container - stayed ungated. Move the step to mysql-integration, immediately after its checkout so the sdk-java-workflow pin still holds. This also clears the three Hosted real-process gates pins, which reject a conditioned step among the setup steps that precede managed_dependencies in hosted-harness-mysql, and reflow the new test to the repo's prettier style. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvya43o17i * style(ci): drop the blank line left behind in hosted-harness-mysql Moving the disk floor gate out of hosted-harness-mysql left a stray blank line between its checkout and 'Set up Java'. Restoring it makes the whole workflow diff additive on mysql-integration only, so 'hosted-only jobs are unchanged' holds byte for byte and an A/B census of that job reads clean. Co-authored-by: Qwen-Coder <[email protected]> * ci(sdk-java): record the real workflow size after the disk-floor gates The six #16003 disk-floor gate steps grow sdk-java.yml from 154074 to 155650 bytes. main had already drifted 3742 bytes past the recorded 150332 — inside the 4096 allowance, but it left only 354 bytes of ratchet headroom, so this real growth tripped Lint & Static. Record the file's actual size rather than shrinking the gates: each one is a three-line step calling .github/scripts/check-disk-floor.sh, and the workflow test pins all six gated jobs. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw02ewzoa2 * fix(ci): derive the disk-floor corpus from runs-on and narrow the broker claim Address the two blocking review findings on the Java disk-floor gate: R1-1: the meta-test skipped any job without the gate and compared a closed six-name list, so a newly pool-routed lane that forgot the gate stayed green, and a neutered gate (continue-on-error) kept every assertion true. Derive the corpus from each job's runs-on (same hazard as the setup-java corpus), require gateIndex > -1 per pool-capable lane, and assert the gate carries no continue-on-error. The derived set is the same six jobs today. Verified by mutation: adding continue-on-error to a gate reddens it, and renaming a gate (ungated pool job) reddens it with 'expected -1 to be greater than -1'. R1-2: the step comment claimed RUNNER_TEMP covers the broker store, but the absolute free-KiB floor cannot express the broker's used-ratio refusal (a 952 GiB volume at 94% used still has ample free bytes). Narrow the comment to the gate's real scope (near-full filesystem / inode exhaustion, #10035) and state the broker disk-threshold investigation stays out of scope, matching the PR description. Bump .size-baseline for the comment change (155650 -> 155888). sdk-java workflow meta-test 117 passed / 5 skipped; eslint exit 0, prettier clean; check-workflow-size.sh passes. * fix(ci): calibrate the Java disk floors and fail closed in the meta-test Review round on this PR found the gate could not fire in the two shapes it exists for, and that the meta-test could go green through the same drift it was added to prevent. R1-2: every lane ran the shared 2 GiB / 100k-inode default. The #16003 incident was a used-ratio refusal — the broker reported 0.94 while the 952 GiB volume still had tens of GiB free — so no absolute floor can express that threshold, and 2 GiB is below what these lanes write: a host with 4 GiB free passed the gate and then died mid-suite. Give the five heavy lanes the repo's build-sized 8 GiB floor, leave flyway-migrations and the shared value alone, and add an opt-in DISK_FLOOR_MAX_USED_PCT arm to check-disk-floor.sh for the two broker lanes (90%). R2-1/R2-2: the corpus came from `runs-on` text and skipped jobs outside it, so a lane routed through an expression or a job output shipped ungated while the suite stayed green, and nothing asserted the hosted-only jobs carry no gate. Fail closed: an explicit hosted-only allowlist, a gate required for every other job, a total-count assertion, and a negative assertion on those two jobs. Pin the per-lane floors so dropping one is a test failure. Verified: sdk-java-workflow suite 117 passed, 5 skipped; it goes red when a gate is deleted, when one is added to a hosted job, and when a floor is removed. check-disk-floor suite 12 passed, including four new ratio cases. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> Co-authored-by: qwen-code patrol <[email protected]> |
||
|
|
b383efb854 |
fix(ci): widen the built-tree download window for eight shards (#16285)
* fix(ci): widen the built-tree download window for eight shards Eight shards now pull the same unit-test-tree artifact at once, so the download cap has to clear the blob's tail latency rather than the median. On main the slow host class took 4m52s, and two consecutive runs expired twice at the five-minute cap before the unpack ran, which failed the required Linux check with no test involved. Raise both attempts to fifteen minutes and pin the pair in the build-to-shard handoff contract. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): size the shard download cap from the measured tail Review flagged that the outgoing comment claimed a pool host had stalled "for over a quarter of an hour", which the new fifteen-minute cap then contradicts. That claim came in with the sharding commit and was never tied to a run; neither incident can prove how far past five minutes the tail went, so the comment now states only the measured distribution. Also correct the number: the slowest completed shard took just under five minutes, and it was a different shard that expired, on two consecutive runs. Add the missing shard-side ceiling contract, mirroring the build job's: every blocking step cap has to fire before the job cap, because a cancelled job skips the failure-gated disk-pressure collector. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
eefd4fd654 |
test(e2e): accept both plan-mode wordings in the ACP case (#16273)
`acp integration > blocks write tools in plan mode` filtered tool updates
with `includes('Plan mode')` and asserted `'Plan mode is active'`. The
unified agent loop blocks through the core scheduler, which says
`Tool blocked by plan mode: ...` (lowercase), so the case-sensitive filter
found no event and the nightly unified leg failed while every push leg
stayed green (run 41771749222).
Match `/plan mode/i` and keep the file assertion that proves nothing ran.
Verified locally: the case fails on origin/main with
`QWEN_CODE_ENABLE_UNIFIED_AGENT_LOOP=1` and the same message CI reports,
and passes both with and without that flag after the change.
|
||
|
|
ad47896d85 |
test(interactive): share one CI-scaled wait policy across the TUI suites (#16276)
Nine interactive suites each declared their own polling helper with a fixed budget (8s, 12s, 15s, 20s) and no allowance for where the suite runs. The shared ECS pool runs several Actions runners per host, and hosted macOS VMs are slower still, so an assertion that passes in ~12s locally expires mid-repaint in CI. The current open reports all have this shape: tui-foreground-shell (#15712, #15814, #16046), mid-turn-submit (#16250) and mods-tool-middleware (#16040). Move the helper to one module and scale its budget 3x when CI is set. A stuck UI still fails on the same assertion, just later; no assertion or product path changes. mid-turn-submit's 30s request wait gets the same treatment: it exceeded that wait at 97s on a loaded host. Verified locally: tui-foreground-shell, mid-turn-submit and tui-editor-parity pass (33 passed, 1 skipped), eslint and the integration typecheck are clean. |
||
|
|
d99bb685dd |
test(web-shell): wait for the restored card before clicking continue (#16286)
The nightly `web-shell Browser Regression` dies in the generated-UI retention case at `expect.poll(() => text).toBeDefined()`: the click on `#continue-analysis` produces no prompt request, so the capture route never fires (run 41771749222, job 126382801964). The same case passes on a dev machine — 6/6 runs here — so this is a host-timing shape, not a product defect. Wait for the card's continue control to be enabled, give dispatch a CI-sized budget, and fail with the card and status state when nothing is dispatched. The next nightly then names the cause instead of `undefined`. |
||
|
|
d24198e866 |
fix(ci): repin the RHEL 10 rpm root to the current stream10 digest (#16261)
The pinned manifest for the RHEL 10 build root no longer resolves on quay.io: `quay.io/v2/centos/centos/manifests/sha256:99b8fe27…` answers 404, so every job using that container dies at "Initialize containers" with `manifest unknown` before a single step runs (run 41759710053, job 126345260531). The tag still exists, and resolves to sha256:511d3584… today. Repin to that digest. Staying on a digest keeps the build root fixed for the life of the release, which is why this was pinned in the first place; only the value moves. |
||
|
|
e1bf0a2c24 |
fix(ci): resume linter archive downloads (#16067)
* fix(ci): resume linter archive downloads Retry truncated actionlint and shellcheck downloads from the bytes already received instead of starting over on each attempt. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): resume only after the first linter download attempt The retry loop passed --continue-at - on every attempt, including the first one, right after the stale local copy was removed. A resume request against a missing file appends only the tail, so the archive failed its SHA-256 pin and the lane died. The PR's own test asserts the opposite: the first request must not resume, retries must. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvy7yxvq7f * fix(ci): bound linter download retries to the CI step budget Three review findings on the resume loop: - Five attempts at --max-time 90 plus 5/10/15/20s of backoff is 500s, which outlasts the 5-minute 'GitHub CI helper checks' step that runs this installer (ci.yml:1433). On a stalled asset host the fourth attempt was cut off by the step, so neither the final exit nor the same step's --actionlint/--yamllint/helper tests ran. Three attempts put the worst case at 285s, in line with the ~273s bound of the curl --retry 2 call this loop replaced. - curl resolves --continue-at - against a missing file to offset 0 and sends no Range header, so the old comment described a hazard that cannot happen and the first-attempt guard it justified was inert on the wire. The comment now states what the loop and the 33/36 branch actually do, and the guard is gone. - Neither the 33/36 rm -f nor the attempt cap was covered: deleting the rm -f and raising the cap to 99 both left the suite green. Two tests now pin them, each verified to fail under its own single mutant. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvymz2cq81 * fix(ci): state the real resume budget and name the failed archive Address the three review Suggestions on this PR's own new code: - Budget comment: 285s is per archive, and the steps that provision both archives inside one 5-minute step can still cut the second one off. Also drop the PR body's "up to five attempts", which never matched the three the code ships. - Terminal silence: the last attempt exits before the retry line, so a continue-on-error run ended on "retrying in 10s..." with no archive named and no exhausted budget. Echo that line, keeping curl's own exit status. - Range-refusal rationale: measured curl exits 33 and leaves the partial byte-identical, so the rm -f is load-bearing because every attempt would otherwise re-send the same offset, not because a second body gets appended. The stub now models that sequence (28 with a partial, then 33 untouched, then a clean full body). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw0d4pwgah --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
4593a3b8f2 |
fix(ci): run desktop smoke headless on rpm roots without Xvfb (#16211)
* fix(ci): give the RHEL 10 desktop smoke a headless display RHEL 10 removed xorg-x11-server-Xvfb from every repository: BaseOS, AppStream, CRB, extras and EPEL 10 contain no such build, so the rpm build-root dependency step failed one step after #16154 fixed the EPEL bootstrap (`No match for argument: xorg-x11-server-Xvfb`, run 41642580745, job 125940965630). Keep Xvfb for the Fedora 44 leg and give the RHEL 10 smoke a real display instead: a weston headless compositor with rootless Xwayland on top, since GTK3 has no headless backend and WebKitGTK always needs a display server. Export WAYLAND_DISPLAY before Xwayland is launched, wait for both sockets with a hard timeout, and pin the app to X11 so the bridge is actually used. Verified end to end on a Desktop Checks dispatch of this head (run 41652880267): the RHEL 10 rpm lane passes dependency installation and the packaged smoke, and the Fedora 44 lane keeps its xvfb-run path. The three workflow meta-tests that parse this file pass (161 passed, 1 skipped). * fix(ci): key the rpm display arm on the matrix and harden its meta-test Review round 2 findings on #16211: - R2-1: the display arm keyed on an artifact_suffix literal while the EPEL bootstrap it depends on keys on matrix.epel. Declare matrix.display ('xvfb' on Fedora 44, 'weston' on RHEL 10) and branch on that, so a leg added later picks a server instead of inheriting an arm by accident. - R2-2: the xvfb-run arm shipped a shell function through declare -f into a child bash without -e -u -o pipefail. Run the same npm command in both arms so both keep the step's error regime; assert no declare -f remains. - R2-3 / R1-4: bound the shared package list by the conditional that follows it instead of a prose comment, ignore comments when checking it, and assert the xvfb/weston arms, the weston socket name against WAYLAND_DISPLAY, and the Xwayland display number against DISPLAY and the X11 socket. * fix(ci): fail the rpm display arm closed and pin the smoke's wiring R2-1: the install arm keyed on `matrix.display` but defaulted every unknown value to the weston arm, which only works where the EPEL bootstrap above has run. Turn it into an explicit `case` with an error arm, and assert from the matrix that a non-Fedora root taking the weston arm has EPEL enabled, so a new leg with `display: 'weston'` and `epel: 'false'` turns the meta-test red instead of dying at `dnf install` in a release run. R1-4: restore the two pins the previous rewrite dropped (`weston --backend=headless-backend.so`, `XDG_RUNTIME_DIR`) and bind the rest to the step's structure rather than to bare substrings: the weston readiness wait must sit between the two launches, the helper must `return 1` on timeout, and the EXIT trap must reap both daemons. Each added assertion was checked against the mutation it names (all five turn red; the unmutated tree stays green). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw1dkg4zbx --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
b5975157d1 |
fix(ci): drop the healthy-default apt gate and prefer the VPC intranet mirror (#16260)
* fix(ci): drop the healthy-default gate so a fast apt burst cannot veto the mirror The merged probe (#16218) kept a third gate: override only when the default measured below 250 kB/s. On the mainland hosts the deb.debian.org path is bimodal — run 41743539611 on hz4-2 probed 372 kB/s (a fast 512 KiB burst), so the gate kept the default, and the sustained 200 MB transfer then throttled to 45 kB/s and hit the 60-minute cap again. Remove that gate. The two mirror-side gates already cover the cases it was meant to protect: the 1 MB/s absolute floor rejects a "slow vs slower" override, and the 4x factor keeps a genuinely fast default (the Hong Kong hosts) on the built-in source. The hz4-2 sample (default 372 kB/s, mirror 1.97 MB/s) now selects the mirror; the Hong Kong and both-slow shapes are unchanged. The contract test pins the floor, the factor, and the absence of any healthy-default gate. * fix(ci): prefer the VPC intranet apt mirror to bypass the capped public egress The public mirrors still traverse the pool's capped 100 Mbps public egress that throttles under concurrent load — the exact problem configure-maven-mirror.sh solves for Central by preferring mirrors.cloud.aliyuncs.com, which answers from inside the VPC and consumes no public bandwidth. Add it as the first apt probe candidate. Off the VPC (hosted runners, Hong Kong hosts) it is unreachable, the probe measures 0, and a reachable public mirror wins — so no host map is needed and the probe self-verifies on each build. |
||
|
|
80953e053b |
test(integration): give the ACP parity rig a scratch runtime directory (#16224)
* test(integration): give the ACP parity rig a scratch runtime directory TestRig points QWEN_RUNTIME_DIR at `<testDir>/.qwen-home`, and the parity rig forwarded only QWEN_HOME to both ACP surfaces. Session runtime state (`tmp/<hash>`, token usage) therefore landed inside the workspace, so the folder-structure snapshot embedded in each request listed `.qwen-home/tmp/<hash>` for the surface that inherited the rig's runtime and not for the other: `expect(shared.requests).toEqual(current.requests)` then failed whenever that ordering broke, and the retry went green (run 41668631944, first attempt failing three parity cases). Point QWEN_RUNTIME_DIR at the same scratch home as QWEN_HOME so neither surface writes into the workspace. Verified with the fixed bundle: 24/24 parity cases pass. With KEEP_OUTPUT, the test directory now holds `.qwen-home/settings.json` and `trustedFolders.json` only, where origin/main additionally creates `.qwen-home/tmp/<hash>` for the same case. * test(integration): keep the ACP parity scratch runtime under KEEP_OUTPUT Checkpoint for review suggestions R1-1/R1-2 on #16224. The parity rig now keeps its scratch QWEN_HOME when KEEP_OUTPUT is set, matching TestRig.cleanup()'s truthiness check, and pins the absence of the default `<testDir>/.qwen-home/tmp` runtime directory directly instead of relying on the request-snapshot diff. Not verified locally: `npm run bundle` cannot resolve the generated web-templates sources in this worktree, so the parity case was not executed. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw14zt15bk --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
aaf523cfc4 |
fix(ci): probe the apt mirror so mainland E2E hosts can build the sandbox image (#16218)
* fix(ci): probe the apt mirror so mainland E2E hosts can build the sandbox image The docker E2E leg builds the sandbox image per push under a SHA-scoped tag, so on any host whose image store no longer holds a recent build, the two apt-get layers are downloaded again. deb.debian.org serves the Hong Kong hosts at MB/s and the mainland-China hosts at 11-20 kB/s, which turns that build into 2-3 hours against the job's 60-minute cap. Over the past week 97 of 127 docker legs on the ecs-qwen-hz* hosts were cancelled this way, and the run reports `cancelled` instead of a verdict. Probe rather than pin a mirror or a host-name map: the build measures deb.debian.org and two domestic mirrors, and passes --build-arg APT_MIRROR only when the default is slower than 250 kB/s and a mirror beats it by 4x. The image keeps deb.debian.org as its default source, so release builds, local sandbox builds and the healthy Hong Kong hosts are unchanged, and no fleet inventory needs to be maintained. The probe's measurements are logged, so the next slow build can be diagnosed from its own output. Refs #16214 * fix(ci): require an absolutely fast mirror before overriding the apt source The relative guard alone only asked a mirror to be 4x faster than the default, so on a host where both endpoints were slow the mirror still "won" and moved the host onto a source it never needed. Require 1 MB/s in absolute terms too: below that the build is slow either way, and the extra cache key is not worth it. * test(ci): pin the apt-probe threshold values, not just the gate structure The new contract test asserted the gate variables and comparisons but not their values: mutating slow_default 10x up or fast_mirror 10x down left the suite green while flipping the both-slow host shape onto a still-slow mirror (review finding on #16218). Pin both literals with a word boundary — a plain substring pin would still match the 10x-up mutation — and witness both mutations reddening the suite before restoring the values. * test(ci): require the mirror ARG in both Dockerfile stages A single-match assertion survived deleting the runtime-stage ARG APT_MIRROR (R1-1, mutation-witnessed by review): the builder declaration satisfied the regex while the runtime apt layer silently kept the slow default. Count both declarations instead; deleting either stage's ARG now reddens the suite. |
||
|
|
b09e821485 |
test(integration): install the local extension fixture from a subdirectory (#16222)
TestRig.setup() now points QWEN_HOME at `<testDir>/.qwen-home`, and this
case installed `<testDir>` itself as the extension. An install copies its
source tree into the extension store, so the destination sat under the
source and fs.cp refused it: every E2E shard has failed on
|
||
|
|
d6b3e1fca7 |
fix(core): attribute monitor and workflow notifications to the registering turn (#16177)
* fix(core): attribute monitor and workflow notifications to the registering turn Preserve the turn that starts monitors and workflow runs in their completion metadata so ACP sessions can consume results at the next tool batch instead of waiting for the turn to end. Add registry coverage for attributed and unattributed registration, plus Session coverage for same-turn delivery and earlier-turn admission. Co-authored-by: Qwen-Coder <[email protected]> * fix(core): keep caller turn and document same-turn injection scope Address the three review suggestions on the registering-turn attribution: - Prefer the invocation-context promptId over promptIdContext in both registries, and cover that arm: a daemon/ACP session holds different values in the two stores, so a fallback-only implementation would stamp results with an id the consumer never matches while staying green. - Assign `sourceTurnId` with `??=` so a caller-supplied turn survives registration, mirroring the adjacent `todoWorkChainId` assignment, and pin that with a case per registry. - Reword the design decision record in both language versions: same-turn injection applies to any notification whose origin turn equals the live turn, not only to subagents with origin metadata. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmw08uebbab * test(cli): cover bridge-context notification delivery Pin monitor and workflow same-turn consumption under the invocation context used by daemon and ACP prompts, and align the paired design records with all background task kinds. Co-authored-by: Qwen-Coder <[email protected]> * test(cli): cover notification injection boundaries Cover monitor and workflow delivery at a tool continuation, align monitor callback fixtures with the required event count, and pin unattributed results to automatic admission. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): repair type errors in the same-turn tool-continuation test R3-2: the tool-continuation case added for the monitor/workflow notification thread did not typecheck, breaking the packages/cli build: - the execute mock was declared zero-argument but called with an AbortSignal; accept an optional signal parameter. - sendMessageStream's message is PartListUnion (string | Part[]), so .some and .filter do not exist on the string branch; narrow with the file's existing 'as Part[]' pattern (also resolves the implicit-any on the callback param). Type-only change; tsc --noEmit clean for the file, same-turn suite 10/10, eslint exit 0, prettier clean. --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
35aa4b76ae |
fix(core): require explicit trust for settings-file project hooks (#16196)
* fix(core): require explicit trust for settings-file project hooks A repository .qwen/settings.json SessionStart hook ran at startup with no trust prompt while the folder-trust feature was off by default: getProjectHooks() consulted isTrustedFolder(), which returns trusted when the feature is disabled. Gate it on isExplicitlyTrustedFolder() instead, matching the frontmatter-hook and LSP gates from #16182. Update the hooks docs, and grant explicit trust in hook test fixtures. Refs #14746 Co-authored-by: Qwen-Coder <[email protected]> * style(core): use Array<T> for the tuple-array cast Co-authored-by: Qwen-Coder <[email protected]> * test(integration): grant explicit trust in ACP parity fixtures The ACP parity rig spawns the CLI with its own QWEN_HOME, so the trust granted by TestRig.setup() never reaches those sessions. With project hooks now requiring explicit folder trust, the UserPromptSubmit hook scenarios silently lost their hooks. Seed the scenario home with folder trust enabled and the rig directory trusted, matching the tool-hook suites. Co-authored-by: Qwen-Coder <[email protected]> * fix(core): align project-hook trust with the interactive boundary Follow the Claude Code model: interactive sessions keep the explicit folder-trust gate, while non-interactive invocations (-p, piped stdin, ACP/SDK embeddings) treat trust as implicit — the caller chose the directory and there is no prompt surface. This keeps platform embedders working without trust-file migration. Surface a startup warning when interactive project hooks are withheld so the gate is not silent, and document the headless contract. Revert the integration-fixture trust grants that the headless exemption makes unnecessary. Refs #14746 Co-authored-by: Qwen-Coder <[email protected]> * test(integration): drop ACP parity trust seeding made moot by headless exemption The parity rig spawns non-interactive ACP sessions, which now treat trust as implicit, so the seeded trustedFolders.json only perturbed the request-snapshot comparison (an extra tmp/ tree under .qwen-home). Restore the fixture to its main-branch shape; all 23 parity cases pass. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): tolerate partial settings shapes in the project-hook warning The startup warning read settings.workspace.settings.hooks without optional chaining; AppContainer tests render with a partial LoadedSettings mock, and a missing workspace scope crashed the effect before config initialization completed. Guard the lookup; all 272 AppContainer tests pass. Co-authored-by: Qwen-Coder <[email protected]> * docs(hooks): resolve trust-gate wording contradictions (R1-5) Troubleshooting step 3 still stated the legacy trusted-folder rule and the restart list omitted security.folderTrust and persisted trust decisions. Align both with the interactive explicit-trust gate and the headless implicit-trust contract, and mention the withheld-hooks startup warning and browser state. Co-authored-by: Qwen-Coder <[email protected]> * test(integration): drop dead ACP trust seeding in the tool-hook suites The per-session trustedFolders.json written beside the QWEN_HOME override is never read: getTrustedFoldersPath() prefers the inherited QWEN_CODE_TRUSTED_FOLDERS_PATH that TestRig.setup() stubs. Both blocks sit in front of an --acp spawn, and ACP sessions are non-interactive, so the headless exemption already loads project hooks without any trust grant. Remove the writes and the now-unused realpathSync import, restoring both suites to their base shape. Refs #14746 * test(cli): pin the folderTrust derivation behind the project-hook gate The interactive gate is covered by the Config table, but nothing pinned settings.security.folderTrust.enabled -> Config.folderTrust. Assert that loadCliConfig derives it and defaults it to false, so a derivation flipping to true cannot silently arm repo-supplied hooks. Verified the case goes red when the default is mutated to true. Refs #14746 * docs(hooks): align trust-boundary descriptions with implementation Remove the unimplemented claim that withheld project hooks appear in the hook browser. Clarify that loop.md retains the legacy folder-trust predicate while executable project hooks use explicit interactive trust. Co-authored-by: Qwen-Coder <[email protected]> * fix(cli): keep folder trust out of workspace reach The project-hook gate reads security.folderTrust.enabled from merged settings, so a repository could enable the very flag that authorizes its own hooks and then reach any pre-existing TRUST_FOLDER or TRUST_PARENT rule for the directory. Restrict the key to the operator scopes, beside allowPrivateNetworkHooks, and pin both directions: a workspace can neither enable the gate nor disarm a user's setting. Also close the smaller review notes. The startup warning no longer blames trust when disableAllHooks removed the hook system entirely, and it now has coverage for the warned and both silent branches. Every hook fixture ran pre-trusted, so add an interactive integration case that declines trust and asserts the hooks never fire, the warning names the remedy, and the tool result still reaches the model unmarked. Drop the duplicated node:fs import and correct the docs claim that IDE trust decisions apply live to settings-file hooks. Refs #14746 Co-authored-by: Qwen-Coder <[email protected]> * test(cli): align the daemon trust-grant case with the workspace restriction security.folderTrust is now stripped from workspace scope, so a grant on a workspace that only self-enables trust reports the disabled state instead of refusing an ineffective grant. Keep the alias DO_NOT_TRUST fixture: if the restriction regresses, the grant throws again and this case goes red (mutation-verified). Fixes the Test (ubuntu-latest) failure on |
||
|
|
e77f73e973 |
fix(vscode): keep the embedded-bundle guard failure a readable assertion (#16210)
When the guard fires on the ~32 MB embedded webview bundle, a failing not.toContain makes vitest serialize a multi-megabyte diff; the worker dies with RangeError: Invalid array length before the assertion can render, and the Vite server close hangs. Check marker presence as a boolean with a named message instead: same pass/fail contract, readable AssertionError on failure. The small-bundle guard keeps not.toContain, where a diff is cheap and useful. Closes the last open finding on #10695 (ic:5503336042). |
||
|
|
dff7deb19c |
docs(release): explain hosted-runner queueing for publish (#16194)
* docs(release): explain hosted-runner queueing for publish * docs(release): link the current release pool record |
||
|
|
a8e6c2ee57 |
ci: scope triage finalize concurrency to PR runs (#16070)
* ci: scope triage finalize concurrency to PR runs
Co-authored-by: Qwen-Coder <[email protected]>
* test(qwen-triage-finalize): compare the concurrency group with whitespace flattened
The group is a folded YAML scalar, so its rendered value keeps a newline
next to the more-indented continuation line. Pinning the literal string made
the assertion fail on the PR's own workflow file (Test (ubuntu-latest,
Node 22.x) at
|
||
|
|
af318939bf |
fix(serve): address remote-access review findings R1-6/R1-7/R1-9/R1-11 (#16199)
Follow-up to #15887, fixing four review findings that were merged unresolved (each labeled Suggestion but a real defect), with red->green regression tests: - R1-6: the remote-access route catch-all echoed the raw internal error message (e.g. an absolute server path from a cert ENOENT) to the client and logged nothing. Now logs the stack to the daemon stderr and answers a generic message; RemoteAccessError keeps its intentional 409 code/message. - R1-7: a successful status poll never cleared a prior poll/action error, so the role=alert banner stayed on screen indefinitely. accept() now clears it. - R1-9: the polling effect was keyed on availability and reset the whole draft on every blip, wiping typed origins/port and the revealed token. Split the identity-scoped reset from polling so a transient availability blip preserves the operator draft. - R1-11: the interface Select guard (!== 1) admitted zero interfaces and rendered an empty dropdown. Now renders only for >1 (0 shows noNetwork). Witness: each new test fails without its fix and passes with it; RemoteAccessSettingsCard 6/6, remote-access route 4/4. eslint + prettier clean. |
||
|
|
0f22726809 |
fix(core): honor Anthropic transport timeout configuration (#16193)
Co-authored-by: zzj <[email protected]> |
||
|
|
1b689dd4be |
feat(serve): enable remote access from settings without restarting (#15887)
* feat(serve): enable remote access from settings without restarting Add a locally managed, independently authenticated listener with live revocation and session continuity. Explain connection setup and reflect daemon availability in the sidebar. Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): expose isDaemonMixedContent in the boot-test daemon mock Co-authored-by: Qwen-Coder <[email protected]> * fix(serve): scope the Local Control pairing secret to its own listeners `GET /workspace/local-control` returned the unredacted pairing URL and QR to any bearer-authenticated request. The secondary listeners serve this same Express app and each verifies only its own token, so a remote-access client reached the Local Control pairing credential — a secret belonging to a different listener, which disabling remote access never revokes. Gate the unredacted branch on the listener that owns the credential (primary or local-control) as well as on operator authority, and pin both arms: a remote-access bearer gets `urlRedacted: true`, the owning listener still gets the URL. `POST /workspace/local-control/disable` stays open to remote-access callers. That is now a stated decision rather than an inherited default: the bearer already reaches every other route on this app, so denying it here would be security theatre while breaking remote management, and the response is redacted by the gate above. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvz8eo3a8v * fix(cli): tear down a permission-blocked prompt at disconnect grace expiry The preserve-running-prompts branch returned early for any live promptAbort, including a prompt blocked on a client permission vote registered while the stream was live. Once the remote entrance is revoked the vote can never arrive (permissionResponseTimeoutMs defaults to 0), so the turn never settled, promptSettled never ran, hasRunningDetachedPrompt stayed true, and sweep plus both connection reapers skipped the connection forever — pinning an ownedSessions entry and one of the maxConnections slots until restart. Gate the preserve branch on hasPendingClientRequest: a session with an outstanding permission vote falls through to the existing teardown, which abandons the pending request and detaches the bridge client. A prompt that is not waiting on the client is still preserved unchanged. Witness: the new regression test fails without the condition (abandon callback never fires: [] vs ['bridge-req-1']) and passes with it; the existing 31-minute preserve test stays green (65/65). --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
bc19325983 |
feat(web-shell): add host mount entries and align managed empty-state layout (#15928)
* feat(web-shell): 增加 managed-host 构建入口与托管会话页 为产品侧集成提供独立的 managed-host 产物(--mode managed-host)与托管会话页: 不挂 daemon workspace/session context,托管会话由 provider 直接渲染列表、会话与输入框。 包含:vite lib 构建模式、managed-host 渲染入口及测试、ManagedSessionsPage 托管分支与 测试、provider 类型调整、README 与中英文设计说明。本次不涉及宿主注入能力。 * feat(web-shell): 托管宿主可注入侧栏导航 props ManagedAgentWebShell 增加可选导航 props 并原样透传给 ManagedSessionsPage: navigationTarget / archivedNavigationTarget / onArchivedSessionsStatusChange / navigationActive / navigationSearch。 宿主据此决定是否把官方会话列表 portal 到自己的侧栏(navigationTarget 为 null 时 仍走 home 布局但不 portal),并同步搜索词与归档计数。类型复用 ManagedSessionsPage 现有 props,不新增重复定义;新增 2 个用例覆盖原样透传与未传时保持 undefined。 * feat(web-shell): add full-page app-host entry and align managed empty-state layout - Add app-host mount entry (renderWebShellApp) so a non-React host can render the complete WebShell (sidebar + welcome + composer) by injecting baseUrl/provider; no shell is hand-drawn and no backend address is hardcoded. - Center the managed empty state like the standalone chat (content shrinks so the chatPane centers it) instead of pinning the welcome to the top and composer to the bottom; gate the welcome header off when a failed session is selected. - Surface session failure and unconfirmed-submission notices together above the composer, dedupe the same message across channels, and style them as callouts. - A pending prompt owned by another session now offers a redirect to the owning conversation instead of retry/discard from a foreign session; reconcile tests. - Build app-host with global (unscoped) CSS so root-level state classes match. * test(web-shell): align managed status smoke assertions Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): reach the owning conversation before retrying a foreign pending A pending prompt owned by another conversation no longer offers retry in the foreign session: it renders "Open the original conversation" instead. The spec still clicked "Retry the same request" straight from the foreign session, so the two foreign variants timed out waiting for a button that is deliberately absent from this head while the owner variant passed. Follow the redirect first, then retry. Owner variant unchanged. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvwo8rnm58 * docs(web-shell): document the app-host entry and drop ignored e2e links The review on head |
||
|
|
1f09497f45 |
fix(core): require explicit trust for project frontmatter and LSP (#16182)
* fix(core): require explicit trust for repository execution * test(integration): explicitly trust skill hook fixtures * test(lsp): add isExplicitlyTrustedFolder to document-sync mock config The document-sync tests create a bare Config mock with only isTrustedFolder. The new explicit-trust gate in NativeLspService and LspServerManager calls isExplicitlyTrustedFolder(), which is undefined on the mock, producing TypeError at reload time. Add isExplicitlyTrustedFolder: () => true alongside the existing isTrustedFolder mock so the 13 affected cases pass again. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
31f64eb2e9 |
test(cli): pin same-turn shell notification delivery (#16176)
* test(cli): pin same-turn shell notification delivery Co-authored-by: Qwen-Coder <[email protected]> * style(cli): format the same-turn shell delivery test Lint & Static failed on this head inside the incremental prettier step, naming only Session.test.ts. Reflow the new `notifyShell` cast with the repository's own prettier (3.6.1, matching the ^3.5.3 pin); the hunk is whitespace only, ten lines re-wrapped, no assertion or call changed. Co-authored-by: Qwen-Coder <[email protected]> * test(cli): assert same-turn shell active-work hold Shell holds are reported by category rather than by shell id. Move the check ahead of the delivery assertions so removing the consumed notification leaves the worker active and fails at the assertion meant to witness it. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e4cf95e774 |
feat(config): add operator model catalog refresh switch (#16066)
* feat(config): add operator model catalog refresh switch Honor a settings-level refresh switch from operator scopes, including bare mode, and skip the background models.dev request when usage statistics are disabled. Workspace settings cannot re-enable the refresh, and the existing environment-variable off switch still wins. Co-authored-by: Qwen-Coder <[email protected]> * style(config): make catalog refresh change set prettier-clean `Lint & Static` failed in the incremental Prettier step (run 41303341242, job 124838743484) on six files that this PR itself touches: - docs/users/configuration/settings.md - packages/core/src/config/config.ts - packages/core/src/config/config.test.ts - packages/cli/src/config/config.test.ts - packages/cli/src/config/execution-sandbox-settings.test.ts - packages/cli/src/config/settingsSchema.test.ts Applied `prettier --write` with the repo config (prettier 3.6.1, the version pinned in pnpm-lock.yaml). Formatting only: no behavior change. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvy5ts5g7c * fix(cli): mirror modelCatalogRefreshEnabled in mcp reconnect The throwaway Config built by qwen mcp reconnect passed usageStatisticsEnabled but not modelCatalogRefreshEnabled, so the catalog-refresh gate defaulted to on and the command still contacted https://models.dev/api.json even when the user had model.catalogRefresh: false. The env var has no catalog equivalent, so only the settings value is mirrored, matching loadCliConfig's resolution. The reconnect suite passes at this head (38 passed). * fix(config): keep the operator catalog refresh opt-out when a project replaces `model` A trusted workspace that replaced the whole `model` section (`null`, `false`, `[]`, or a string) left `stripSettingKeys` nothing to strip, and `customDeepMerge` then assigned that value over the merged object. The operator's `model.catalogRefresh: false` disappeared, `loadCliConfig` read `undefined`, and core fell back to `true`, so a host that opted out of the models.dev download fetched it anyway. Restore the operator value in `mergeSettings` the same way `tools.executionSandbox` already is, and state that the `privacy.usageStatisticsEnabled` route is not workspace-restricted. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzl9mtg9d * fix(cli): carry the operator catalog refresh opt-out into the sandbox The container mounts only the user settings dir and builds its env passthrough from `process.env`, so a System-scope `model.catalogRefresh: false` never reached the sandboxed CLI and it still fetched models.dev. Set the refresh env var at the sandbox handoff, which covers both the docker/podman passthrough and the seatbelt child env. Also move the operator scope reduce into a shared `selectOperatorCatalogRefresh` beside `selectOperatorExecutionSandbox` instead of keeping a second copy of the rule, and pin that a project which replaced the whole `model` section keeps its own keys. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzpjyde9j * fix(cli): carry the usage-statistics opt-out into the sandbox handoff Core refreshes the models.dev catalog only when both `modelCatalogRefreshEnabled` and `usageStatisticsEnabled` are true, but the sandbox handoff exported the opt-out for `model.catalogRefresh` alone. A System/SystemDefaults-scope `privacy.usageStatisticsEnabled: false` is neither mounted into the container nor forwarded, so the containerized CLI still fetched models.dev despite the documented second opt-out. Resolve the privacy switch with the same helper `loadCliConfig` uses, so the env precedence matches, and export the opt-out when either half is off. Tests: cover the usage-statistics direction and the "no opt-out" negative case, and pin the `model.catalogRefresh` workspace strip by key so deleting the restricted-list entry fails instead of removing an iteration. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvztu9wu9q --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
276a447ef0 |
fix(ci): interleave PR review endpoint cohorts and report final routes (#16151)
* feat(ci): support same-model endpoint splits in PR review canary The endpoint override derived the cohort by comparing the routed model name against the configured canary/default models, so configuring the SAME model on both cohorts — a pure quota split across two gateways — labelled every PR 'default' and silently disabled the override. Derive the cohort from the bucket predicate itself instead (review_cohort_for_bucket: same clamp, same PR-number bucket test as the model router), so model name equality no longer matters. The notice's cohort field remains the attribution when both cohorts run one model. * fix(ci): interleave review endpoint buckets and report final routes Co-authored-by: Qwen-Coder <[email protected]> * style(ci): apply Prettier to the review endpoint bucket test The Lint & Static lane runs `node scripts/lint.js --prettier` on the files a PR touches, and this PR's own test file was not formatted: three `expect(...).toBe(...)` lines exceed the print width, so the incremental lint step exited 1 on every head. Formatting only - no assertion or fixture changed. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzj4h0j9a * fix(ci): share the endpoint host derivation and pin the cohort notice - Extract openai_base_host() so the proxy bypass and the cohort notice derive the host from one place; the notice cannot name a host the traffic was not routed through. - Drop the comments that still describe the removed model router. - Pin the notice's bucket count (8% -> bucket 87/100), make the noKey/badUrl rows exact strings, and assert initial_openai_host so the three mutations that survived this suite now fail. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvznesli9g --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
5b7f9ec1c3 |
fix(ci): repair desktop RPM dependency and teardown checks (#16154)
* fix(ci): repair desktop rpm dependency and teardown checks Co-authored-by: Qwen-Coder <[email protected]> * test(desktop): use a counted space class in the EPEL checksum pin `scripts/lint.js --eslint` runs on every file the PR touches, and the new rpm dependency test spelled the two spaces in the pinned `echo` line as literals: `no-regex-spaces` rejects them, so Lint & Static failed on the incremental ESLint step. `[0-9a-f]{64} {2}` matches the same two spaces the workflow emits; the assertion still pins exactly what the release step writes. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzj4h0j9a * fix(desktop): keep the runtime group as a teardown target Teardown collected daemon pids through the zombie-aware liveness predicate and escalated SIGKILL only on leaders that were still alive, so a leader that had already exited — reaped, replaced, or an unreaped zombie — silently dropped its process group, which is teardown's only handle on it. The children the leader spawned (MCP servers, tool subprocesses) then survived and kept writing under the workspace about to be deleted, and the smoke reported a pass. Collect every recorded pid, escalate on every collected group after the drain, and keep the zombie-aware predicate on the "survived teardown" report so the false positive this PR exists to fix stays fixed. The new fixture drives a runtime whose leader exits while a writer in its group ignores SIGTERM. Also pins the EPEL download URL and digest literals, drops the duplicate local liveness predicate in favour of the shared helper, and covers the signal-probe error arm (ESRCH gone; EPERM/EACCES alive, the rule the core helper documents). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzpjyde9j * fix(desktop): decide teardown on the runtime group, not its leader The round-2 escalation signals the recorded pid's group (`kill(-pid)`), but the verdict and the drain still asked `isProcessAlive(pid)`, which answers "gone" as soon as the leader exits — the exact topology `SMOKE_FIXTURE_EXIT_AFTER_SPAWN` reproduces. In that state `waitForExit` returned on its first poll, so the 5 s drain was skipped and the `survived teardown` branch was unreachable. `isTeardownTargetAlive` probes the group first and falls back to the leader where there is no group to signal (win32), so the drain and the leak verdict cover the children the group can still hold. Also drops the "mirrors the canonical … keep both in sync" claim from the desktop probe's comment: it diverges from core's `isPidAlive` on the pid <= 0 guard, the zombie exclusion on the EPERM/EACCES arm, and the vanished-/proc answer. The comment now states those differences and why teardown wants the dead-on-ENOENT direction. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzrp44z9n * fix(desktop): stop counting unreaped zombies as a live teardown group `waitForExit` asks `isTeardownTargetAlive` whether a daemon's group still exists. `kill(-pid, 0)` also answers for members that can no longer write, so a daemon that exited and that the container's PID 1 has not reaped kept its group "alive": the drain burned its five-second window, the second probe still returned the pid, and teardown reported "the packaged runtime survived teardown" for a runtime that was already gone. On Linux the group probe now counts only members that can still write: scan `/proc` for entries whose pgrp is the recorded pid, and report the group stopped when every member is a zombie. A scan that observes no member keeps the group probe's answer, so a hidden /proc cannot end a drain early, and the group handle still wins over the leader handle. Measured on a real zombie group (setsid + _exit, parent never reaps): /proc state Z, `kill(-pgid, 0)` exit 0, `isProcessAlive` false, and `isTeardownTargetAlive` true before this change, false after it. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvzy4lf49w --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4dce337ea2 |
feat(ci): let the PR review canary cohort carry its own endpoint (#16130)
* feat(ci): let the PR review canary cohort carry its own endpoint The review canary split (QWEN_PR_REVIEW_MODEL_CANARY/PERCENT) only moves the model name; both cohorts share REVIEW_OPENAI_BASE_URL and its key, so a rate-limited canary model also burns the shared gateway's quota — the shape behind the 429-storm review timeouts. Add an optional endpoint pair for the canary cohort: QWEN_PR_REVIEW_BASE_URL_CANARY (variable) + REVIEW_OPENAI_API_KEY_CANARY (secret). When the bucket routes to the canary and both are set, base URL and key move with the model and the canary host joins NO_PROXY. A URL without its own key, or an unparseable URL, degrades to the default cohort with a warning — forwarding the default key to another provider would leak it. A canary model identical to the default model disables the override (buckets are indistinguishable). The cohort notice now prints the resolved cohort so rollouts stay attributable. Unset, behavior is byte-for-byte today's: model-only split on the shared endpoint. * fix(ci): drop inert canary NO_PROXY append and record the workflow's new size The canary endpoint override appended its host to NO_PROXY, but nothing in this job consumes it: configure_qwen_network unsets HTTPS_PROXY/https_proxy/ HTTP_PROXY/http_proxy before the override runs, qwen does not consult NO_PROXY when building its proxy agent, and the gh/git wrappers that do get the proxy back only talk to github.com. Removing the two exports also removes the comment that claimed a proxy-bypass symmetry the step cannot produce. Lint & Static was red in "Check workflow file size": 276955 bytes against a recorded 272192 with a 4096 allowance. The growth is the feature itself (the env pair, the override block, the resolved-cohort field), not prose that could move into a sibling file, so the baseline moves with the file rather than the rationale being cut to fit. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvz69ico8s * refactor(ci): simplify canary endpoint validation Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: qwen-code patrol <[email protected]> |
||
|
|
b142071ab2 |
fix(core): report redirects on cd segments (#16129)
* fix(core): report redirects on cd segments Preserve redirect-derived operations when resolving cd, pushd, and popd segments so Write rules see targets opened before the cwd changes. Co-authored-by: Qwen-Coder <[email protected]> * test(core): rename stale cd redirect test title The title said 'ignores redirects attached to cd' but that is the behavior this PR removes; the row survives only because >/dev/null is filtered. Rename it to describe what it actually pins now. --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
c2921217ef |
ci: gate on the OS temp directory and shorten /tmp retention to 1d (#16126)
* ci: gate on the OS temp directory and shorten /tmp retention to 1d
The 2026-10-31 hz incident: /tmp tmpfs hit 100% inodes while the
workspace and RUNNER_TEMP sat on a data disk with millions free, so
every disk-floor gate was green and jobs died on mkdtemp ENOSPC
mid-suite. The gate now always checks the OS temp directory
(deduplicated against caller arguments); the tmpfiles policy drops
7d->1d because the measured 120-260k files/day steady state exceeds
the tmpfs inode ceiling at 7d. Mutation-verified: dropping the
os.tmpdir() check reds the suite.
* test(ci): cover the TMPDIR-unset default and pin the 1d retention
Review findings: the new OS-temp test never executed the production
incident path (TMPDIR unset -> /tmp default) — a stripped default
survived the suite green; and the 7d->1d retention change had no test
coverage — a revert shipped the incident again. Add the unset-TMPDIR
sub-run, a set-equality dedup assertion, and a qwen-docker-cleanup
suite pin on the exact tmpfiles line. Mutations verified red.
* test(ci): pin RUNNER_TEMP apart so the /tmp fallback test bites
The first version of the TMPDIR-unset test let the RUNNER_TEMP default
(/tmp) satisfy the assertion, so a stripped ${TMPDIR:-/tmp} default
survived green — the exact regression the test exists to catch. Pin
RUNNER_TEMP to a distinct directory; the /tmp sample can then only come
from the OS-temp append. Mutation now reds.
* test(ci): unset TMPDIR as empty string; destructure was a no-op
The previous fix still failed on clean trees: spreading a destructured
env cannot DELETE a key — process.env.TMPDIR re-entered via runGate's
own spread, so the /tmp fallback never fired and the test failed
against the correct script. An empty-string TMPDIR is bash-unset for
${TMPDIR:-/tmp} (and bare for the regression), which is the exact
distinction under test. Clean 8/8; stripped-default mutation reds.
* ci(ecs-runner): enforce the /tmp retention from the owned daily service
Review finding: the 1d policy's enforcement agent was the distro's
systemd-tmpfiles-clean.timer, which can be masked on hardened images and
reports 'static' (unverifiable via is-enabled) on stock Ubuntu — with
nothing in the qwen stack running systemd-tmpfiles --clean, the measured
120-260k files/day steady state regrows past the tmpfs inode ceiling and
the 2026-10-31 incident class recurs with every test green. Add
ExecStartPost=systemd-tmpfiles --clean --prefix=/tmp to the daily
qwen-docker-cleanup.service, pin it in its test suite, and document the
enforcement owner in the README verification block. Mutation-verified.
|
||
|
|
a983503f02 |
fix(core): attribute shell notifications to the registering turn (#16125)
A background shell that settles mid-turn never reached the model until the turn ended: ShellNotificationMeta carried no sourceTurnId, so Session.#takeCurrentTurnBackgroundParts — which skips queued items whose sourceTurnId differs from the current turn — could never match the item, and delivery fell through to the end-of-turn drain. Capture the registering turn in BackgroundShellRegistry.register(), mirroring BackgroundTaskRegistry (getInvocationContext()?.promptId ?? promptIdContext.getStore()), declare it on ShellTask, and forward it through ShellNotificationMeta into the Session shell callback. Both the direct is_background: true spawn and the foreground→background promote funnel through register(), so one assignment covers both; a promoted shell is attributed to the promoting turn, which is the turn that ends up waiting for the result. Monitor and workflow callbacks have the same gap; left as a follow-up. Guarded by two registry tests: the completion meta carries the registering turn, and a shell registered with no turn context stays unattributed — and still emits — rather than inventing an attribution. Patrol-Run: qwen-issue-patrol/jmvz3emo2tv Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
e7b86ae32d |
fix(mcp): warn about disabled tool typos on every discovery path (#16120)
Refs #16078 Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
38b71299c8 |
feat(desktop): build and publish rpm installers (#16060)
* feat(desktop): build and publish rpm installers
Add Fedora 44 and RHEL 10 release roots, strict RPM smoke checks, and artifact naming that keeps each root distinct.
Co-authored-by: Qwen-Coder <[email protected]>
* fix(desktop): declare rpm runtime requirements and clear the yaml gate
The rpm legs built artifacts with no `Requires:` entries: Tauri's rpm bundler
writes them only from `bundle.linux.rpm.depends`, which nothing set, so
`verifyRpmDependencies` had no webkit soname and no glibc floor to find and the
release could not pass its own gate.
- Add `rpm-fedora44.conf.json` / `rpm-rhel10.conf.json` overlays declaring the
webkit soname and each root's glibc floor (2.43 vs 2.39 cannot share one
config), and pass the per-leg overlay to `tauri build --config`.
- Quote-free `container: ${{ matrix.container || null }}` stays as is with an
inline yamllint disable: quoting the expression would turn a null container
into an empty string. The shape test pinning that literal moves with it.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy1jgjo76
* ci(desktop): record the rpm legs in the workflow size baseline
`desktop-release.yml` is now 49858 bytes, past its recorded 45089 plus the
4096 allowance, so `Lint & Static` fails on the ratchet before any linter runs.
The growth is the two rpm legs: their matrix entries, the per-leg overlay
wiring, and the roots' comments. Moving that prose out would not pay for it —
the legs themselves are the bytes — so the number moves with the file in the
same PR, as the gate asks.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmvy1jgjo76
* style(desktop): apply prettier to the rpm legs and realign the workflow shape pin
Prettier flagged .github/workflows/desktop-release.yml and
packages/desktop/scripts/smoke-linux-installers.js on head
|
||
|
|
8aea64d5f7 |
fix(desktop): verify AppImage library closure (#16062)
Read every ELF's DT_NEEDED entries from the extracted AppImage and fail release when a dependency is neither bundled nor part of AppImage's host-provided baseline. Document the contract and cover the failing and repaired cases. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
5df87f7080 |
fix(desktop): stop the bundled runtime from the Windows installer (#16071)
An update started by a desktop build released before the app-side runtime stop can still leave the bundled Node daemon holding node.exe while NSIS replaces the runtime. Clear that lock from the installer pre-install hook by image path, wait boundedly for the image to become writable, and abort before copying unless PowerShell returns an explicit success. Pin the behavior in the desktop release gate. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
292c0da525 |
test(install): pin Windows PATH persistence contract (#16072)
* test(install): pin Windows PATH persistence contract Co-authored-by: Qwen-Coder <[email protected]> * test(install): format the pinned PATH assertion Prettier collapses the single-string expect(...).toContain(...) call at 76 columns, which the incremental lint gate rejected on the previous head. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvygjkzk7s --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
73a7268447 |
test(cli): guard settings reference against schema drift (#16085)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
0ebbe15221 |
fix(review): name the model half in the anchor re-run and drop the retired range model (#16094)
* fix(review): name the model half in the anchor re-run and drop the retired range model F1 (rc:3808708929): the marker-recovery re-run sentence named only `--since <sha>`, while the cache path two paragraphs above requires the pair `--since <lastCommitSha> --since-model <lastModelId>`. A reader who applied just the re-run sentence handed `fetch-pr` a sha with no model half, which is exactly the anchor the same-model gate exists to rule on. The sentence now carries `--since <sha> --since-model <model>` and says the model half rides the same replacement rule, so the existing "REPLACING any --since it already carries, never appending a second one" semantics are unchanged. F2 (rc:3810474869): the `effective: true` bullet still called the report's diff and plan `since..head` — the full-range model the incremental narrowing retired. It now names the sliced scope the report publishes (`deltaFiles`, widened by `interaction[]`), consistent with the `incremental.scope` wording in the same bullet. Both are pinned by new assertions in SKILL.test.ts: the re-run sentence is sliced to itself before asking for `--since-model` (the same words occur in the cache path, where they were already correct), and the `effective: true` bullet is sliced to the next bullet and asserted free of `since..head` while still naming the three scope classes. Refs #9654 Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-issue-patrol/jmvyftv3bt8 * docs(review): take both anchor values from the side file on the re-run The marker-recovery re-run said to pass "exactly as the command above carries it", but on this path that command carries no --since-model (no cache) or another model's (cache anchor withheld) - so a literal reader was refused as cross-model-anchor and reviewed the full diff, the very failure the recovery path exists to remove. Both values now come from the side file's sha/model pair, and the replacement rule names both flags. The adjacent pin moves with the sentence in the same commit. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvyioqrq7v --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
91bce47a14 |
fix(vscode): bucket session history by calendar week (#16097)
The session history dropdown grouped entries with a rolling 168-hour window while the buckets are labelled Today / Yesterday / This Week / Older. On a Monday, a session from the previous Saturday was still within 168 hours, so it showed up under "This Week" even though the calendar week had only just started. This Week now begins at the Monday-first start of the current calendar week, derived from the same local day boundary as the Today/Yesterday buckets. The boundary uses setDate rather than millisecond arithmetic so a DST transition cannot shift it. Sessions before that boundary fall through to Older, and Today/Yesterday are untouched. Regression coverage pins a Monday with a previous-Saturday session (Older), plus a Friday/Wednesday positive control proving This Week still exists mid-week so the Monday assertion cannot pass on a missing group. Patrol-Run: qwen-issue-patrol/jmvyk46lrtc Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
4700dd2368 |
fix(web-shell): put the list-failure notice under the tabs, not above them (#16104)
* fix(web-shell): put the list-failure notice under the tabs, not above them #16082 merged with the notice as the nav's first child, so a failed session load rendered above the Sessions/Archived tab bar and pushed the list's own controls down - an error about the list appearing before the list's navigation. It now renders after the tabs and the title filter, in the region the sessions occupy. Also stops break-all from cutting the request id mid-token ('req' / 'uest-list-500'); break-words keeps it whole and wraps at a boundary. Observed in a real browser at 1280x800 against a 500 from /sessions/query: before: nav children = [alert y=52, tabs y=180, search y=212] after: nav children = [tabs y=52, search y=84, alert y=128] The file also carried two prettier violations from the squash merge; prettier --write normalises them, which is why the diff touches two unrelated indents. * style(web-shell): keep the diff to the move and the word break The previous commit ran a newer prettier than this repo pins, which reindented two unrelated call sites and does not satisfy the repo's own --check. Redone from the pristine file with the pinned prettier (3.6.1): the net diff against main is now only the block move and break-all -> break-words. --------- Co-authored-by: yiliang114 <[email protected]> |
||
|
|
9dde2f657d |
fix(web-shell): surface Managed session list failures (#16082)
* fix(web-shell): surface Managed session list failures Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): match the list-failure sentences, not the whole alert The list-failure alert wraps its message and its Retry button in the same role=alert div, so textContent reads 'Managed sessions could not be loaded.Retry loading sessions'. Two assertions compared that concatenation against the sentence alone, which made four cases fail with a message that looked identical on both sides. Assert the sentence is present, the way the sibling HTTP-500 case already does, and keep the alert count so the 'once' property is still pinned. Not verified locally: this checkout's core dist predates managed-runtime/managed-permission-rules.js, so the suite cannot load here. CI is the first real run. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
dd020ddfcc |
fix(web-shell): sanitise goal approval text and edit paths (#16080)
* fix(web-shell): sanitise goal approval text and edit paths Co-authored-by: Qwen-Coder <[email protected]> * style(web-shell): let Prettier place the diff-path chain The Lint & Static gate runs prettier --check and this test file failed it on a member chain that wraps differently. Formatting only. Not fixed here: neutralises controls in the edit diff path still fails on this branch (expected '+1-1' to be the sanitised path), on the unformatted original as well, so it predates this commit. * test(web-shell): reach the diff path past DiffView's own wrapper The case asserts that a crafted edit path has its zero-width joiner, ANSI escape and BEL rendered as visible escapes. It walked from the File diff region up one level and took the first child, which is DiffView's stats element - hence the '+1-1' the assertion reported. The path sits one level higher, as the sibling of DiffView's root, so the traversal needs one more parent. Selector only; the assertion and the sanitisation it exercises are unchanged. * fix(web-shell): tolerate a diff block without a path DiffBlock.path is optional, but the new sanitise call passed it straight to sanitizeControlChars, which takes a string. tsc -p tsconfig.lib.json rejected it as TS2345 at ToolApproval.tsx:727, which is what took down Lint & Static, Test (ubuntu), Integration Tests, web-shell E2E and Capture visuals on the same head. Use the file's existing optional-string shape ("?? ''") so a path-less block still renders an empty label, as before. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvy7yxvq7f --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
dfeae12de0 |
fix(core): use the amend delta for loosely spelled amend commits (#16073)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
1ac4f6cd1b |
fix(sandbox): install fonts-dejavu-core in the runtime image (#16032)
The capture card renderer in scripts/verify-capture.mjs requests DejaVu Sans Mono, but the runtime image installs no font packages at all. Patrol-Run: qwen-issue-patrol/jmvx4obxsrz Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
7f774219ae |
test(opentui): run the Windows preview installer suite through cmd.exe (#16028)
* test(opentui): launch the Windows preview entry through cmd.exe The PowerShell preview installer preservation suite failed on every nightly Windows run: the seeded qwen.cmd launcher printed nothing, and the leftover batch child kept the file locked so teardown hit EBUSY. PowerShell was only a way to run a .cmd from Node. Its `&` call plus `exit $LASTEXITCODE` returned before the batch child wrote anything, so run the entry through cmd.exe instead (`shell: true`). PowerShell also writes its own startup cache under %LOCALAPPDATA%\Microsoft\Windows\PowerShell. The fixture pointed USERPROFILE at the home tree the assertions treat as installer-owned, so every successful test failed on that cache. Point APPDATA and LOCALAPPDATA outside the compared home. * test(opentui): compare home files, not shell-created folders With the launcher fixed, every case in the suite now reaches the preservation assertion and fails on Windows because PowerShell creates its own empty %USERPROFILE%\AppData folder shells on first run. Those are the shell's account directories, not installer output. Redirect APPDATA/LOCALAPPDATA outside the fixture home (so the startup cache file stays out too) and compare only the home's file contents. --------- Co-authored-by: yiliang114 <[email protected]> |
||
|
|
eaafba5d14 |
ci: route the classify_pr gate to ECS for every pull request (#16019)
The gate is the needs-prerequisite of every Linux lane, but its own runner stayed association-gated: untrusted fork PRs queued the whole chain on the scarce hosted pool (observed 30+ min on PR #15851). classify executes only trusted base-SHA code with a read-only token — a strict subset of the lint exposure accepted on 2026-10-31 — so route it unconditionally to ECS, kill-switch excepted. The ubuntu_runner association gate for the test/integration lanes is unchanged. Routing battery: gate matrix across all associations, structural pin against clause creep; association-clause mutation reds the suite. |
||
|
|
cca888dfcc |
fix(core): bump MCP client and core to 2.4.0 to drop pre-bundled fast-uri 3.1.0 (#15925)
* fix(core): bump MCP client and core to 2.4.0 to drop pre-bundled fast-uri 3.1.0 The MCP client 2.0.0 pre-bundles ajv with an inlined fast-uri 3.1.0, which dependency audits cannot see and overrides cannot reach. 2.4.0 re-bundles fast-uri 3.1.8, clearing CVE-2026-6321, CVE-2026-6322, CVE-2026-18446, CVE-2026-75975 and CVE-2026-76172 from the published bundle. Both packages stay exact-pinned in lockstep because the client pins core to the same version. Fixes #15919 Co-authored-by: Qwen-Coder <[email protected]> * chore(vscode-ide-companion): regenerate NOTICES for MCP client/core 2.4.0 Co-authored-by: Qwen-Coder <[email protected]> * fix(core): refuse redirects before the SDK, not only inside the fetch 2.4.0 resolves a same-origin 30x in its own layer, against requestInit.redirect, before the compatibility fetch is called. The Agent Plugin transport only set headers, so a configured or bearer credential was replayed to another path on the same origin. Ask the same question in one place and carry the answer into requestInit on both transport paths, and pin it with a test that fails when the option is absent. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: yiliang114 <[email protected]> |
||
|
|
1886aa7221 |
test(ci): pin the supersede kill-dir prefix to the workflow's (#15963)
The supersede kill dir is minted under RUNNER_TEMP with a fixed prefix and never exported, so the replay's forge stub can only find it by listing that prefix. The literal was written twice — once in the workflow, once in the stub glob — and renaming one side alone left the stub globbing nothing: the forged kill record never landed and the refusal replay still passed without exercising the refusal. Derive the production prefix from the workflow text at module scope and pin the stub glob to it, so a one-sided rename is red instead of vacuous green. The stub case line now consumes that shared fixture string. Patrol-Run: qwen-issue-patrol/jmvvrdn39qo Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
8ca595c687 |
fix(vscode): gate the companion vitest worker cap on the shared pool (#15940)
* fix(vscode): gate the companion vitest worker cap on the shared pool packages/vscode-ide-companion was the one guarded config that raised its test ceilings for the ECS pool without touching worker count. Every sibling (core, cli) pairs the timeout ternary with a maxWorkers one, so a 25%-of-cores vitest run on a host carrying several concurrent jobs was the only thing keeping this workspace from adding to the oversubscription it was already widening its timeouts for. Add the same ternary core carries — '25%' on `ecs-qwen-*`, undefined otherwise — so the cap exists only where the contention does. scripts/tests/unit-vitest-configs.test.ts pins it from both sides in the autofix gate clamps case: the ECS arm now reads the companion config alongside core/cli and expects the gate's own --maxWorkers value, and a second, separately re-imported arm with RUNNER_NAME unset expects `maxWorkers` to be undefined. The off-pool arm is what makes the pin load-bearing: a flattened `maxWorkers: '25%'` passes every pool-lane assertion, and only the second import round sees it (mutation-checked). Refs #10695 Co-authored-by: Qwen-Coder<[email protected]> Patrol-Run: qwen-issue-patrol/jmvva8cvtq7 * test(vscode): correct the config-clamp comments the reviewer disproved The reviewer was right on both statements I added: the autofix gate does not launch vscode-ide-companion, and normal CI does not run this workspace unlimited. run-unit-test-lanes.sh exports VITEST_MAX_THREADS (halved for the side lane, 2 where unset) and vitest resolves poolOptions.threads.maxThreads before config.maxWorkers, so the config value is a default for runs carrying no such env, not the lane ceiling. Comments only; the clamp and its pins are unchanged. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvw0o04b4b * test(vscode): move the companion worker-cap pin to the bundle-guard block The review disproved the clamp-array coupling: the gate passes its clamps on the command line for whatever changed packages it launches, so the companion config does not need to track that array. The bundle-guard block already re-imports this config per runner name; extend it with the maxWorkers expectation (ECS '25%', foreign and unset undefined) instead of a second stub/reset/import round in the clamp test. The unset arm keeps the stronger mutation coverage. Clamp block restored to its pre-PR membership and comment. --------- Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
54d47667d2 |
refactor(review): build the all-zero DriveReport through one factory (#15943)
`runDrive` returned the same all-zero report — `unavailable`, nothing observed, no exit code, empty output — from four separate exits, each a hand-copied literal that differed only in its `note`. A field added to `DriveReport` later would land in some of those copies and not others. Add `notStartedReport(note)` and route those four exits through it. The two exits that DID start something — the readiness timeout and a failed `new-session` — carry live `readyAfterMs` / `killedStale` and keep their own reports, unchanged. Refs #9695 Patrol-Run: qwen-issue-patrol/jmvvcdimeq9 Co-authored-by: yiliang114 <[email protected]> |
||
|
|
5af73b6f09 |
fix(review): route isAoneHostFamily through the shared host normalizer (#15934)
* fix(review): route isAoneHostFamily through the shared host normalizer
`isAoneHostFamily` re-inlined the exact normalization chain
(`toLowerCase()` + strip `:\d+$` + strip trailing dot) that
`normalizeHostSpelling` already owns, so the helper's contract comment —
"both host predicates route through it" — was false for the third
predicate. The two copies were character-for-character equivalent, so
behaviour was identical; this is a consistency defect, not a live bug.
Route the family predicate through `normalizeHostSpelling`, leaving the
helper, the canonical write router and the `.alibaba-inc.com` wildcard
untouched.
Covered by a new `isAoneHostFamily` describe block; the paired mutation
witness (helper returns `host + '.invalid'`) leaves those assertions
green before the change and turns them red after, while the
hostsEquivalent/isAoneCanonicalHost control legs fail in both runs.
Refs #9601 (finding rc:3820959694)
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmvv8372lq5
* style(review): quote the new isAoneHostFamily test name per prettier
The family-predicate case added in
|
||
|
|
ca7d487ce9 |
ci: route Lint & Static to ECS for every pull request (#15974)
* ci: route Lint & Static to ECS for every pull request * ci: isolate HOME and complete routing pins for lint_and_static - lint_and_static admits untrusted forks to the shared pool, so pin a job-level HOME/USERPROFILE under runner.temp and create it before the first write (git safe.directory, npm config). - Derive the classifier output from each job's own runs-on in the ceiling helper, so a timeout pointed at the wrong output throws instead of being substituted; parameterise runnerOn the same way. - Pin test's own runs-on to ubuntu_runner and add the HOME-isolation guard for every lane routed past the association gate. - Correct the qwen-autofix premise and the integration_no_ak comment, and record the routing decision in a bilingual design note. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvwdojb45 * fix(ci): isolate lint lane HOME and restore routing guard strength Resolve R1-1 (Critical): per-job HOME for lint_and_static so untrusted fork code cannot plant git/npm config a later trusted job inherits; shared warm caches re-pointed via getent + XDG_DATA_HOME/COREPACK_HOME. Resolve R1-2: timeout ceilings derive the output name from each job's own runs-on. Resolve R1-3: explicit ubuntu_runner pin for test. * docs(ci): record lint ECS fork-routing design and correct stale premises Resolve R1-5: bilingual design record (container tradeoff, acceptance, residual risks), qwen-autofix.md section 38 premise, no_ak scoping comment. * fix(ci): publish run-unique isolated HOME from step env, not job env Resolve P1/R1-1: jobs.<job_id>.env cannot read the runner context, so the workflow failed GitHub validation entirely; publish HOME/USERPROFILE via GITHUB_ENV from the first step instead. Resolve R2-1: RUNNER_TEMP survives across jobs on this pool, so the path is run-unique, cleared before create, and reclaimed by an always() step before the failure collector. Resolve R2-3: share content-verified linter/corepack caches via RUNNER_TOOL_CACHE. Resolve R2-5: move the HOME guard into the PR-gated routing battery with a single-lane set pin; resolve wenshao R2-4 with one fleet ladder for both outputs. Witness: actionlint 1.7.12 exits clean on ci.yml; six mutations each red the matching guard. * docs(ci): correct lint routing record per round-2 review Supersede link points at qwen-autofix.md section 38 (R2-4); read-only token and HOME-relative write class stated precisely (R2-2); residual risks are examples not a census, naming web_shell_e2e_smoke and other PAT pool jobs (wenshao R2-3); association gate recorded as throughput policy, not an enforceable boundary (R2-6); dispatch-over-fleet priority and per-lane hosted ceilings (P3); cache cost list and PR-gated test placement updated. * chore(ci): bump ci.yml size ratchet for merged tmpdir routing and HOME isolation Growth is real: #15979's helper-test tmpdir routing arrived via the main merge the freshness gate required, plus this PR's run-unique HOME mechanism. 156377 -> 161281 bytes. * fix(ci): keep COREPACK_HOME per-run; shared cache is a poisoning channel Corepack does not re-verify extracted entries on cache hit (reproduced locally on 0.36.0 with the repo's pnpm pin: tampered entry executes with the recorded hash unchanged, network disabled). Move COREPACK_HOME inside the run-unique HOME; XDG_CACHE_HOME stays shared because lint.js SHA-256-verifies linter archives on every hit. Guard and bilingual record updated; shared-corepack mutation reds the guard. --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: Shaojin Wen <[email protected]> |
||
|
|
b3c392235a |
fix(core): keep session-commit registration for an absolute cd into the same repo (#15953)
* fix(core): keep session-commit registration for an absolute cd into the same repo
`cdTargetMayChangeRepo` returned true for ANY target starting with '/',
with no reference to the shell's cwd. The product-recommended spelling
`cd /abs/path/to/this/repo && git commit -m "..."` therefore latched
`cwdShifted`, `attributableInCwd` went false, `trackSessionCommit` never
ran, `sessionCommitShas` stayed empty, and the agent's own next
`git commit --amend` was hard-blocked by the amend guard.
Resolve an absolute target against the shell cwd and treat the cwd itself
or a subdirectory of it as in-repo. Sibling and out-of-tree targets still
shift, including the `<repo>-sibling` case that a bare `startsWith` would
wrongly admit. Relative targets are untouched, and resolution stays static
(`path.resolve`) — no `git rev-parse` in the walk.
`gitCommitContext`, `isAmendCommit` and `findAttributableCommitSegment`
now take the cwd; all four module-private call sites thread it through.
Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-issue-patrol/jmvvn3bibqk
* fix(core): refuse an absolute `cd` carrying an unresolved shell reference
The containment test for an absolute `cd` target resolves statically, so
`cd /repo/$PKG` sits lexically under `cwd` while bash lands wherever
`$PKG` expands. The absolute branch returned before the env-var guard, so
that spelling was certified as in-cwd and the Co-authored-by trailer was
spliced onto a commit in another repository — a regression against the
pre-#12514 `target.startsWith('/') -> true` short-circuit.
Check `$`/backtick before the absolute branch, and correct the docblocks
plus the `/commit` prompt sentence that still described absolute `cd` as
never rewritten.
R1-1, R1-3, R1-4 from bot review round 1 on
|
||
|
|
11b40836d3 |
fix(ci): exempt non-TUI packages from the parity impact gate (#15937)
* fix(ci): exempt non-TUI packages from the parity impact gate * fix(ci): exempt the manifest-less SDK trees from the parity gate The exclusion list was audited from workspace manifests, so packages/sdk-java, packages/sdk-python and packages/zed-extension — none of which carries a package.json — stayed outside it and still selected the component lane for a PR touching only them. Add the three trees to the exclusion, pin each with a single-file case in the classifier contract suite, and record why they are hand-listed in the design doc pair. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvf8ed23g * fix(ci): name the policy exclusion and cover the CUA driver tree The exclusion branch skipped every matched path before recording a reason, so a PR excluded on policy reported the same "No TUI impact" as a PR whose paths the classifier never mapped. Record the reason once inside that branch (R1-2). packages/cua-driver is outside the --cli-only closure: node-repl declares @qwen-code/cua-sdk, but the build filter pulls node-repl without its dependencies and no TUI suite imports the SDK. Exempt it with a single-file row and a rename row (R2-1). The workspace-excluded trees were a third hand-maintained copy of the set pnpm-workspace.yaml states, with nothing reconciling them. A new case reads pnpm-workspace.yaml and asserts every excluded tree still classifies as no-impact (R2-2). The design docs claimed the excluded packages are never built by these gates. npm run bundle compiles integrations/external-context into dist/mem0 through hard-coded esbuild entries, so say what actually happens and name esbuild entries and copy_bundle_assets copies as inputs the manifest audit cannot see (R2-3). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvnt1g33t * test(ci): reconcile workspace negations by value, not literal spelling * test(ci): pin the policy reason for every excluded workspace tree R4-1: the reconciliation test only asserted outputs.component, the value the classifier starts with, so narrowing reasons.add(...) for any tree except cua-driver silently reverted the human-facing diagnostic to "No TUI impact" while all 163 tests stayed green. - assert outputs.reason inside the loop, not only for the CUA driver tree - extract excludedTrees(yamlText) and feed it a fixture with a trailing-comment negation and an !integrations/... negation, so the by-value derivation is exercised for the spellings its comment claims Witness (both mutants, unmutated suite 163/163 green): - reasons.add narrowed to packages/cua-driver/: reason assertion red - derivation reverted to the old literal regex: fixture assertion red Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvs3czk3z --------- Co-authored-by: Shaojin Wen <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
cd4c917fcf |
fix(ci): route helper-test tmpdir off the host /tmp tmpfs (#15979)
* fix(ci): route helper-test tmpdir off the host /tmp tmpfs * test(ci): pin the helper-battery tmpdir routing against comments Two gaps in the #15979 change: - The pin used toContain/indexOf on the raw run block, so prefixing the export with `# ` disabled the routing while the guard stayed green. Filter comment lines before asserting, like the lint-history pin already does, and pin the private dir + EXIT trap the step now creates. - The step routed TMPDIR straight into the shared RUNNER_TEMP root. Copy the unit lanes' second half too: mktemp -d under RUNNER_TEMP and reap it on EXIT, so a cancelled battery does not leave its fixture tree behind (its own after() hook does not run on a signal). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvvyiud548 --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
46d825d115 |
Revert "fix(ci): isolate web-shell smoke from shared runner networks (#15886)" (#15961)
This reverts commit
|
||
|
|
5f5664e596 |
test(managed): treat targeted cleanup loads as non-resuming recovery (#15926)
The crash audit classified every post-failure cleanup load as a failed cancellation takeover because cleanup names a prompt other than the client prompt. Both cleanup branches return before any resume, so a targeted cleanup is a non-resuming recovery mode; keep requiring at least one accepted cancellation takeover. Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
504556b6a9 |
ci(e2e): time-box the artifact download retry and the two uncapped legs (#15917)
Each leg downloads the shared build archive twice: once, and again from the retry that only fires when the first attempt failed. Only the first attempt carried timeout-minutes, so a retry that stalls held its runner with no ceiling at all — up to GitHub's 360-minute default. Run 37119220504 (#13284) already showed both attempts lost inside one window on a macOS shard while its sibling downloaded the same artifact. Two limbs: - Give the four 'Download build artifact (retry)' steps timeout-minutes: 10. The value is not new: the four first-attempt downloads in the same jobs already pin 10. - Give e2e-test-macos and isolated-nightly a job-level timeout-minutes: 60. The two other artifact-consuming legs (e2e-test-linux, e2e-interactive-opentui) already use 60, and 60 is also the ceiling the shard-retry budget gate in run-e2e-tests.sh takes its 25-minute reserve out of. `build` keeps its own 30 — it produces the archive rather than consuming it. web-shell-browser-regression has the same missing job ceiling but is left alone: it is outside this finding's scope and does not consume the build artifact. The retry steps keep their absence of continue-on-error — the first attempt's continue-on-error is what makes the retry reachable, and copying it onto the retry would let a stalled download pass silently into the unpack step. isolated-nightly's job-level continue-on-error is untouched, so its 60-minute ceiling frees the runner without changing the leg's conclusion. Pinned in scripts/tests/e2e-workflow.test.js: both limbs, the retry's visible failure, and a regression guard over the already-landed values (build 30, the two consuming legs 60, the four first-attempt steps 10). Refs #12132 Patrol-Run: qwen-issue-patrol/jmvuv88frpw Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
38337f7257 |
test(managed): count only accepted cancellation takeovers (#15912)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
cc6de38cb5 |
fix(ci): isolate web-shell smoke from shared runner networks (#15886)
* fix(ci): isolate web-shell smoke from shared runner networks Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): clarify hosted smoke font guard behavior Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): explain dormant smoke pool steps Co-authored-by: Qwen-Coder <[email protected]> * docs(ci): stop overstating which dormant smoke guards are pinned The retained comment claimed every self-hosted step's guard is pinned by checkout/preflight tests. Only two of six are: `Recreate runner Git metadata` (scripts/tests/qwen-runner-checkout-workflow.test.js) and `Use pre-installed Node.js (self-hosted)` (scripts/tests/no-ak-integration-ci.test.js). `Check host font baseline (self-hosted)` is named by no test file, and its `runner.environment == 'self-hosted'` guard can never be true under `runs-on: 'ubuntu-latest'` — so it is parked, not pinned. Say which two are pinned and that the font baseline guard is parked, and re-measure the ci.yml size ratchet entry (155625 -> 155708). Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvugxtx22a --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
5cba899e46 |
docs(ci): correct the qwen-autofix job cap and budget table to match the workflow (#15914)
Two entries in .github/workflows/qwen-autofix.md restated review-address bounds that the authoritative workflow no longer has: - af-039 said "The 300-minute job cap puts the worst case at 5 runner-hours per slot (100 across the fleet at 20)". The cap is timeout-minutes: 345 (qwen-autofix.yml:3670), so it becomes 5.75 runner-hours per slot and 115 across the fleet at the max-parallel fallback of 20 (:3679). - af-050's worst-case budget table listed "Repair 20" and "worst case 280 => job timeout 300". The Repair deterministic rejection step is bounded at 70 minutes (:5540, "the 60-minute budget below plus the same 10-minute margin"), so the row becomes 70 and the table's own six rows sum to 330, quoted against the real 345 cap. The trailing "on ubuntu-latest, whose own ceiling is 360" clause is left as-is: 345 < 360 keeps it coherent, and re-deriving the conditional runs-on expression (:3669) is out of scope. The table total is 330, leaving 15 minutes against the 345 cap. The workflow comment at :3667-3668 separately describes a 25m reserve for the four-step unarmed chain (130+60+70+60 = 320), which excludes the setup and report rows this table itemises. The two are deliberately not conflated here. Adds a test that derives every quoted figure from qwen-autofix.yml and requires the doc to agree, so neither entry can drift silently again. The existing sum check reads only the workflow and structurally cannot see a doc that restates the same numbers in prose. Witness: - red, doc unmodified: "x keeps the af-039 and af-050 budget prose in step with the job cap -> expected '# `qwen-autofix.yml` - design record...' to contain '345-minute job cap'"; Tests 9 failed | 327 passed | 14 skipped (350), rc=1 - green, both sites fixed: that test passes; Tests 8 failed | 328 passed | 14 skipped (350), rc=1. The 8 are pre-existing local-environment noise, identical to the unmodified-base baseline (Tests 8 failed | 327 passed | 14 skipped (349), rc=1). Patrol-Run: qwen-issue-patrol/jmvuqxwvapt Co-authored-by: yiliang114 <[email protected]> Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
7c9e79e70b |
perf(release): reuse verified native audio prebuilds across releases (#15854)
* perf(release): reuse verified native audio prebuilds across releases Every release rebuilds the five native audio prebuilds even when nothing that affects those binaries changed. The build is short; the wait is not. For v0.26.3 the Linux x64 prebuild queued 74 minutes to execute for 50 seconds, and the collection step queued again after it — and `publish` depends on that collection, so the whole release sits behind it. Fingerprint the audio build inputs and reuse an artifact from an earlier successful official main release when the fingerprint matches. The key covers the audio package, the dependency lock, the package manager pin and the contents of the producing workflow and this helper, so the Node pin and the prebuildify target arguments are part of it. The package version is excluded deliberately: no part of the native build reads it, and including it would make reuse impossible between releases. Reuse means executing bytes an earlier run produced, so provenance is checked rather than assumed: exact fingerprint name, unexpired, same repository for both run and head run, and a completed successful release.yml run on main from workflow_dispatch or schedule. The pinned download-artifact already verifies the artifact digest. Retention is checked differently at selection and consumption. Selection requires 48 hours of headroom so the artifact outlives the release; consumers require only that it has not expired. Re-applying the margin against a later clock would reject an artifact the release had already committed to, after the native matrix was skipped and with no fallback. Lookup, trial download and payload validation are all non-fatal, and the reuse ID is published only once a trial download has validated; an empty ID runs the full matrix, which is today's behaviour. The publisher does not trust that decision: it recomputes the fingerprint from its own checkout, re-verifies the source run, and validates the payload again, fatally — by then the matrix was skipped, so a bad artifact must stop the release. This does not depend on ECS_RELEASE_HOST_RUNNER_ENABLED and takes effect with the switch unset. The collector needed three fixes to be a trustworthy producer. Its `rm -rf -- "$GITHUB_WORKSPACE/packages/audio-capture/prebuilds"` resolved through whatever `packages` was on a persistent host: with a leftover symlink it deleted an external tree and exited 0, and the following download wrote through the same symlink. It now wipes through a shared preflight that canonicalises the workspace and refuses paths resolving outside it. It also ran `node` with no Node contract, inheriting Node from the hosted image by accident. And its release-only label pinned `x64` while the runners holding that label are ARM64, which never matches and queues until timeout. Contract tests pin the wiring, each confirmed to fail against an injected regression. Design doc in both languages. * fix(release): reconcile audio reuse workflow contracts Share the pre-checkout wipe, preserve the workflow size cap, and let fingerprint failures rebuild native audio. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): use block-style release permissions Co-authored-by: Qwen-Coder <[email protected]> * fix(release): drop x64 from publish's release-host expression The review's unresolved item: collect got the ARM64 label fix but publish kept ["self-hosted", "linux", "x64", "ecs-qwen-release-host"], which can never match the ARM64 machines holding that label, so the OIDC-carrying job would queue forever once ECS_RELEASE_HOST_RUNNER_ENABLED is true. Same expression as the collect fix, same one-word drop. * docs(release): clarify audio reuse rollout and validation Co-authored-by: Qwen-Coder <[email protected]> * test(release): cover artifact overwrite and contained symlink guards * test(web-shell): include transcript coverage in recovery fixture Bring the fixture correction from #15883 into this branch to address the observed smoke failure. Co-authored-by: Qwen-Coder <[email protected]> * fix(release): make audio cache seeding best effort Keep the validated same-run artifact usable when retaining a keyed copy fails. Pin the reuse wiring and correct the retention and producer fixtures without changing release trust boundaries. Co-authored-by: Qwen-Coder <[email protected]> * chore(test): apply prettier to the audio prebuild fixtures * docs(release): clarify audio reuse run provenance Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: zzj <[email protected]> |
||
|
|
bf074c4ad8 |
test(managed-agent): retry the items read through a lagging projection (#15910)
* test(managed-agent): retry the items read through a lagging projection The public reader answers 503 message_projection_pending while projection catches up, and #15870 documents that as retryable. The crash fixture asserted 200 on the first response, so a coordinator restart -- exactly when projection lags -- failed the gate with 'expected: 200' instead of polling (#15909). Poll the read through that one response and widen the await budget to 60s; every other status still fails the way it did. * test(managed-agent): preserve projection retry bounds and wire contract Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
84aee5b9b7 |
test(web-shell): include transcript coverage in recovery fixture (#15883)
* test(web-shell): include transcript coverage in recovery fixture Co-authored-by: Qwen-Coder <[email protected]> * test(web-shell): complete recovery event fixtures Remove the unevaluated transcript type guard and supply required event terminal flags without changing assertions. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): use runner temp for Web Shell browser smoke (#15884) * fix(ci): use runner temp for Web Shell browser smoke Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): resolve runner temp in the browser step context Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): partition the complete macOS test suite into bounded jobs (#15888) * fix(ci): use runner temp for Web Shell browser smoke Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): resolve runner temp in the browser step context Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): partition the complete macOS test suite into bounded jobs Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): account for the macOS aggregate job budget Record the additional five-minute aggregate without changing any test execution timeout. Co-authored-by: Qwen-Coder <[email protected]> * test(install): isolate Windows fixtures from persistent PATH (#15889) Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): write the macOS shard matrix in block style and pin its gates The seven-row matrix used YAML flow mappings, which `.yamllint.yml` forbids (`braces: forbid: 'non-empty'`), so the `Run yamllint` step cannot go green on this branch. Rewrite the rows in the block style every other workflow matrix in this repo already uses. Add the test pins the shard plumbing was missing: - the `cli`/`core` dispatch argv, including the bare `--` that keeps `--shard` forwarded to vitest instead of consumed by npm, and the per-group `NODE_OPTIONS` choice - `id: 'install'` on `test_macos_shards`, which the suite's `steps.install.outcome` gate silently reads as '' if it is renamed - the aggregate `test_macos` gate against the shards' gate, so the preserved check name cannot report `skipped` while the legs fail Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvu6810z1y * fix(ci): preserve shell fixture gates on macOS system Bash Handle empty issue families under nounset without mapfile and explicitly reject either missing OSS credential. Keep failure assertions and include shell diagnostics. Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): keep macOS aggregate failures eligible for infra retry Co-authored-by: Qwen-Coder <[email protected]> --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
54fd8e0372 |
fix(ci): route the E2E build job through the pool-first runner expression (#15885)
* fix(ci): route the E2E build job through the pool-first runner expression * fix(ci): carry the full self-hosted contract on the E2E build job * test(ci): pin the pool contract's step order and sync the build-routing design note * test(ci): pin the npm-cache position and heal body on every pool job The pool-contract loop only asserted that `Configure persistent npm cache (self-hosted)` exists, so moving it below `Install dependencies` or dropping its `if:` stayed green while the pool install silently fell back to the default npm cache and re-downloaded through the slow egress the step exists to avoid. It also left the build job's ownership-heal body unpinned: the full body was only asserted on the Linux leg, so dropping the `sudo -n` fallbacks from the build copy stayed green while the next checkout on a pool host still holding root-owned leftovers dies with EACCES. Pin the cache step's position and `if:`, and compare every pool job's heal body against the leg copy the docker-lock ownership suite already pins in full. Three mutants (cache step moved below install, cache `if:` stripped, build heal `sudo -n` dropped) now each fail one test; unmutated 130/130 pass. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvu6810z1y * docs(design): pair the E2E build-once note with its zh-CN version docs/design/README.md asks that updating an existing single-language design adds the missing version and aligns the pair, and this PR rewrites the section that records why the build left the hosted runner. Add e2e-build-once.zh-CN.md with the same section order and the same decisions, and link both directions immediately under each title. Also drop the "only hosted-pinned structural job" claim from the English note: `e2e-interactive-opentui` is hosted-pinned too, which the same note already records two paragraphs down. The remaining sentence keeps the actual cause - every leg waits on `build`, and `build` sat on hosted ubuntu. Co-authored-by: Qwen-Coder <[email protected]> Patrol-Run: qwen-pr-closeout/jmvu6810z1y --------- Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
d896c1816a |
test(managed): align recovery and transcript fixture contracts (#15899)
* test(managed): record which load path produced each post-crash entry
HostedProcessCrashIT fails on main with postFailureLoads.every(Boolean), but
the recorded sequence changes shape between runs (#15898: 9 vs 10 entries,
different true positions, one run with none), so the current message cannot
say which of the connector's load paths sent the offending request.
Record a compact origin discriminator per entry — cancellationTakeover,
passiveManagedRuntimeRecovery, driveRuntimeRecovery, mcpManagementOnly and
whether cancellationPromptId was present — and include it in both assertion
messages. The assertion itself is unchanged: 'no tool replay after a crash'
is the property it protects, and the next red run should name its own origin
instead of being guessed at.
* test(managed): name the load path behind each post-crash entry
Both review findings were correct: the origin string added in
|
||
|
|
48a110cea2 |
test(web-shell): select retained radio without pointer coordinates (#15896)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
87022fa7c6 |
test(managed-agent): select managed JDBC in Hosted fault fixtures (#15882)
Co-authored-by: Qwen-Coder <[email protected]> |
||
|
|
70be140e14 |
fix(release): reduce hosted runner queue delays (#15832)
* perf(release): reuse verified native audio prebuilds across releases Every release rebuilds the five native audio prebuilds even when nothing that affects those binaries changed. The build is short; the wait is not. For v0.26.3 the Linux x64 prebuild queued 74 minutes to execute for 50 seconds, and the collection step queued again after it — and `publish` depends on that collection, so the whole release sits behind it. Fingerprint the audio build inputs and reuse an artifact from an earlier successful official main release when the fingerprint matches. The key covers the audio package, the dependency lock, the package manager pin and the contents of the producing workflow and this helper, so the Node pin and the prebuildify target arguments are part of it. The package version is excluded deliberately: no part of the native build reads it, and including it would make reuse impossible between releases. Reuse means executing bytes an earlier run produced, so provenance is checked rather than assumed: exact fingerprint name, unexpired, same repository for both run and head run, and a completed successful release.yml run on main from workflow_dispatch or schedule. The pinned download-artifact already verifies the artifact digest. Retention is checked differently at selection and consumption. Selection requires 48 hours of headroom so the artifact outlives the release; consumers require only that it has not expired. Re-applying the margin against a later clock would reject an artifact the release had already committed to, after the native matrix was skipped and with no fallback. Lookup, trial download and payload validation are all non-fatal, and the reuse ID is published only once a trial download has validated; an empty ID runs the full matrix, which is today's behaviour. The publisher does not trust that decision: it recomputes the fingerprint from its own checkout, re-verifies the source run, and validates the payload again, fatally — by then the matrix was skipped, so a bad artifact must stop the release. This does not depend on ECS_RELEASE_HOST_RUNNER_ENABLED and takes effect with the switch unset. The collector needed three fixes to be a trustworthy producer. Its `rm -rf -- "$GITHUB_WORKSPACE/packages/audio-capture/prebuilds"` resolved through whatever `packages` was on a persistent host: with a leftover symlink it deleted an external tree and exited 0, and the following download wrote through the same symlink. It now wipes through a shared preflight that canonicalises the workspace and refuses paths resolving outside it. It also ran `node` with no Node contract, inheriting Node from the hosted image by accident. And its release-only label pinned `x64` while the runners holding that label are ARM64, which never matches and queues until timeout. Contract tests pin the wiring, each confirmed to fail against an injected regression. Design doc in both languages. * fix(release): move the release build and SDK build off the hosted queue Behind the existing ECS_RELEASE_HOST_RUNNER_ENABLED switch, prepare the version commit and produce the CLI build outputs on an isolated release runner, and split the TypeScript SDK into an isolated producer plus the existing hosted publisher. Eligible Linux downstream jobs join the same pool. With the switch unset every path is exactly today's behaviour. The final npm publication stays on GitHub-hosted runners. npm trusted publishing and provenance require a cloud-hosted runner, so moving the publisher itself would break the release rather than speed it up — only the build work ahead of it can leave the queue. Each migrated job runs one shared preflight before checkout, referenced as a '$/' self-repository action so it resolves at the running commit without a prior checkout. Release-only labels carry no architecture component: the runners holding that label are ARM64, and an "x64" component never matches instead of falling back to hosted. Jobs that genuinely need amd64 — the OSS mirrors and the managed runtime, which execute a hardcoded linux-amd64 ossutil — keep their hosted path, tracked in #15847. The VSCode build routes by matrix.universal so only the dependency-free leg migrates. The SDK publisher consumes the exact tested tarball through a same-run artifact and refuses to overwrite different already-published bytes on retry. Audio prebuild reuse is the base of this branch and is specified separately; the isolated build job is a second consumer of that artifact and is held to the same re-verification contract as the publisher. Rebased onto the audio prebuild reuse branch, so that mechanism and its contract tests are no longer duplicated here. * fix(release): reconcile audio reuse workflow contracts Share the pre-checkout wipe, preserve the workflow size cap, and let fingerprint failures rebuild native audio. Co-authored-by: Qwen-Coder <[email protected]> * fix(ci): use block-style release permissions Co-authored-by: Qwen-Coder <[email protected]> * fix(release): drop x64 from publish's release-host expression The review's unresolved item: collect got the ARM64 label fix but publish kept ["self-hosted", "linux", "x64", "ecs-qwen-release-host"], which can never match the ARM64 machines holding that label, so the OIDC-carrying job would queue forever once ECS_RELEASE_HOST_RUNNER_ENABLED is true. Same expression as the collect fix, same one-word drop. * docs(release): clarify audio reuse rollout and validation Co-authored-by: Qwen-Coder <[email protected]> * docs(release): reserve runner slots on existing ECS hosts * fix(release): quote audio workflow scalar values --------- Co-authored-by: Qwen-Coder <[email protected]> Co-authored-by: zzj <[email protected]> |