Skip to content

feat(desktop): add a linux-aarch64 leg to the Desktop release matrix - #12833

Merged
yiliang114 merged 7 commits into
mainfrom
fix/issue-12806-desktop-linux-arm64
Sep 28, 2026
Merged

yiliang114 merged 7 commits into
mainfrom
fix/issue-12806-desktop-linux-arm64

Conversation

@yiliang114

Copy link
Copy Markdown
Collaborator

What this PR does

Adds an ARM64 Linux leg to the Desktop release matrix so desktop-latest.json gains a linux-aarch64 entry and the release publishes an arm64 AppImage and deb alongside the existing x86_64 ones.

The matrix row is not the whole change, and adding it alone would break the release rather than extend it. Both manifest scripts pick the Linux AppImage by extension alone and require exactly one match, so a second Linux leg makes publish fail after every build already succeeded. This makes the Linux selection arch-explicit in the updater manifest script (which gains the linux-aarch64 key), in the Electron bridge manifest script, and in the Electron bridge's release-assets glob, which now pins the x64 AppImage.

The arm64 leg builds on ubuntu-22.04-arm, not ubuntu-24.04-arm, so both Linux artifacts keep a single glibc floor (2.35). Building it on 24.04 would need glibc 2.39 and would not start on Ubuntu 22.04 or Debian 12 arm64, which the x64 artifact still supports. ubuntu-22.04-arm is a GA GitHub-hosted label with the same 4-core/16GB spec as the x64 standard runner, and jammy/arm64 carries libwebkit2gtk-4.1-dev 2.36.0.

Re-mirroring an already-published release through sync-desktop-to-oss.yml has to be able to reproduce the feed that release actually shipped, so that path — and only that path — passes a new --allow-missing-platform linux-aarch64. The fresh-build path stays strict, so a leg that failed to upload still fails the mirror instead of being published as missing.

Finally, test-release.js gains a check that the matrix's rust targets and the updater manifest's platform keys stay in sync. A leg nobody taught the manifest about still ships its artifact while the feed silently omits it, which is exactly how the reporter's installer ended up pulling an x86_64 AppImage.

Why it's needed

ARM64 Linux has no Desktop artifact at all. The build matrix stops at x86_64-unknown-linux-gnu, so the feed carries four platform keys and an ARM64 machine has nothing native to install. Because the feed offers no arm64 entry and no way to distinguish "no build for my arch" from "no build for Linux", an installer that trusts it falls back to the x86_64 AppImage, which cannot execute without an emulation layer — the install looks successful and the app then fails to start.

The runtime side is already arm64-ready and was written anticipating this target: prepare-runtime.js aliases aarch64-unknown-linux-gnu to linux-arm64, linux-arm64 is in its allow-list, and the root package.json already pins @lydell/node-pty-linux-arm64. Nothing sets QWEN_DESKTOP_TARGET to that value today, so the alias is currently unreachable. ARM64 Linux is already built and shipped elsewhere in this repo (audio-capture-prebuilds.yml and cd-cua-driver.yml both publish arm64 Linux artifacts), and macOS arm64 is the first row of this very matrix.

Reviewer Test Plan

How to verify

The decisive check is that a second Linux AppImage breaks main's scripts, and that this PR makes it resolve unambiguously. Against main:

$ node .github/scripts/create-desktop-update-manifest.mjs --assets <dir with both AppImages> ...
Error: Expected one updater artifact for linux-x86_64, found 2: Qwen-Code-Desktop_0.24.6_aarch64.AppImage, Qwen-Code-Desktop_0.24.6_amd64.AppImage

$ node .github/scripts/create-electron-bridge-manifest.mjs --assets <same dir> --platform linux ...
Error: Expected one Electron bridge artifact matching /\.AppImage$/i, found 2: Qwen-Code-Desktop_0.24.6_aarch64.AppImage, Qwen-Code-Desktop_0.24.6_amd64.AppImage

On this branch both resolve to one artifact each, and the feed gains linux-aarch64.

The contract test is pure Node and needs no install:

$ cd packages/desktop && node scripts/test-release.js
Desktop release helper checks passed.

CI covers the rest: .github/workflows/ci.yml runs node scripts/test-release.js for any PR touching packages/desktop/, desktop-release.yml, or the updater manifest script.

Every changed site was mutation-tested by reverting it individually and confirming a test fails: reverting the updater manifest script, dropping the arm64 matrix row, reverting the Electron bridge script, un-pinning the Electron bridge glob, reverting the mirror flag in sync-desktop-to-oss.yml, removing the null-artifact skip, and widening --allow-missing-platform from a per-platform key to a blanket opt-out. All seven fail; all restored, both suites pass.

Two facts the reasoning depends on were checked against upstream source rather than assumed. Tauri's appimage.rs maps X86_64 => "amd64" and AArch64 => "aarch64", while debian.rs maps AArch64 => "arm64" — so the arm64 AppImage is _aarch64.AppImage and the arm64 deb is _arm64.deb, and neither collides with the x64 names in the merged download directory. And tauri-plugin-updater's get_urls looks up ["{os}-{arch}-{installer}", "{os}-{arch}"] with updater_os() returning linux and updater_arch() returning aarch64, so linux-aarch64 is the key the shipped app will actually request.

Evidence (Before & After)

N/A — release infrastructure, no interactive surface. The user-visible outcome is the published feed, and the live desktop-latest feed was read directly to confirm the gap: desktop-latest.json for desktop-v0.24.6 has exactly darwin-aarch64, darwin-x86_64, windows-x86_64, linux-x86_64, and the release's only Linux assets are Qwen-Code-Desktop_0.24.6_amd64.AppImage and ..._amd64.deb.

UI verification: N/A. No app code path is touched — the change is confined to the release matrix, the two feed-generation scripts, and their tests. The Desktop app binary and its UI are byte-identical for every existing platform; the only new observable is a fifth key in the generated feed plus two new downloadable assets, neither of which exists until a release build runs.

Tested on

OS Status
🍏 macOS N/A
🪟 Windows N/A
🐧 Linux ✅

Linux (x86_64 host): node scripts/test-release.js green; vitest run scripts/tests/{desktop-oss-workflow,release-workflow,workflow-size,hosted-process-ci}.test.js 318 passed / 1 skipped (the skip is pre-existing); prettier --check clean on all seven files; actionlint 1.7.12 clean across all workflows using the repo's own flags from scripts/lint.js. macOS and Windows are N/A — nothing in the diff branches on host OS, and the manifest scripts are arch-independent Node.

Environment (optional)

Local Node v24.19.0 against a worktree of main. No Rust toolchain on this machine, so cargo test (npm test in packages/desktop) and any actual Tauri bundle could not be run locally — see Risk & Scope.

Risk & Scope

  • Main risk or tradeoff: this touches the published release path, so a mistake breaks releases for all platforms, not just arm64. The riskiest single edit is tightening the linux-x86_64 selection from /\.AppImage$/i to /_amd64\.AppImage$/i, which converts a permissive match into an exact one. It is fail-closed and correctly ordered: the manifest is generated before Create GitHub release, so a Tauri rename or a productName change aborts the publish with zero partial artifacts instead of shipping a wrong-arch feed.
  • Not validated / out of scope: the arm64 build itself was never executed here — there is no cargo/rustc on this machine, and a desktop release build cannot be exercised locally. What was verified statically is that the pieces it depends on exist: AppRun-aarch64, linuxdeploy-aarch64.AppImage and linuxdeploy-plugin-appimage-aarch64.AppImage are all published upstream, @lydell/node-pty-linux-arm64 is already pinned, and Node.js publishes linux-arm64 tarballs. desktop-packaging-check.yml calls this workflow with dry_run: true on a daily cron, so the new leg gets a real build, artifact-collection and xvfb smoke run before it ever reaches a release. Note the dry run skips publish, so the manifest scripts are exercised by the test-release.js fixtures rather than by the dry run itself. Runtime behaviour of the produced AppImage on Ubuntu 22.04 arm64 is likewise unexecuted. yamllint could not run locally (the installed 1.28 rejects the repo's .yamllint.yml), so that gate is unverified here and left to CI.
  • Breaking changes / migration notes: none for existing platforms. One operational note a maintainer should be aware of — prepare's already_published probe compares only .version and is arch-blind, so re-dispatching an already-published version to retrofit arm64 is skipped with a ::notice::; that route needs clobber=true. This is pre-existing behaviour and is not changed here. Backfilling arm64 into a historical version is therefore out of scope; this PR makes it present from the next release onward.

Direction review: skipped — the plan-critic agent is not available in the runtime this was prepared in. The runner-label choice (ubuntu-22.04-arm over ubuntu-24.04-arm) was settled on upstream evidence instead: it is GA, has the same hardware spec, and preserves glibc parity with the existing x64 leg rather than raising the arm64 floor.

Linked Issues

Fixes #12806

中文说明

这个 PR 做了什么

给桌面端发布 matrix 增加 ARM64 Linux 构建腿,使 desktop-latest.json 多出 linux-aarch64 条目,发布产物在现有 x86_64 之外同时产出 arm64 的 AppImage 与 deb。

只加 matrix 那一行并不够,而且单独加它会让发布失败、而不是让发布多一个平台。两个 manifest 脚本都只按扩展名挑 Linux AppImage,并要求匹配数恰好为 1,因此多出一条 Linux 腿会让 publish 在所有构建都成功之后才失败。本 PR 把 Linux 的选择改成按架构显式匹配:更新清单脚本(并新增 linux-aarch64 键)、Electron bridge 清单脚本、以及 Electron bridge 那段 release-assets glob(现在显式钉住 x64 的 AppImage)。

arm64 腿构建在 ubuntu-22.04-arm 而不是 ubuntu-24.04-arm,这样两个 Linux 产物共用同一个 glibc 下限(2.35)。若在 24.04 上构建则需要 glibc 2.39,将无法在 Ubuntu 22.04 / Debian 12 的 arm64 上启动,而 x64 产物目前仍支持这些系统。ubuntu-22.04-arm 是 GitHub 托管的 GA 标签,规格与 x64 标准 runner 相同(4 核 / 16GB),jammy/arm64 也提供 libwebkit2gtk-4.1-dev 2.36.0。

通过 sync-desktop-to-oss.yml 重新镜像一个已发布版本时,必须能复现那个版本当时实际发布的 feed,因此只有这条路径会传新增的 --allow-missing-platform linux-aarch64。全新构建路径保持严格:某条腿上传失败时镜像必须失败,而不是把它当成"缺失"发布出去。

最后,test-release.js 新增一条检查,保证 matrix 的 rust target 与更新清单的平台键保持同步。没人把某条腿登记进 manifest 时,产物照常发布而 feed 静默少一个平台 —— 这正是报告者的安装脚本最终拉到 x86_64 AppImage 的原因。

为什么需要

ARM64 Linux 目前完全没有桌面端产物。构建 matrix 止步于 x86_64-unknown-linux-gnu,所以 feed 只有四个平台键,ARM64 机器没有原生安装包可装。由于 feed 既不提供 arm64 条目、也无法区分"没有我这个架构的构建"和"整个 Linux 都没有构建",信任 feed 的安装脚本会退回 x86_64 AppImage —— 没有模拟层它根本无法执行,安装看起来成功,应用随后启动失败。

运行时这一侧其实早已为 arm64 准备好,当初就是照着这个目标写的:prepare-runtime.js 把 aarch64-unknown-linux-gnu 映射为 linux-arm64,linux-arm64 也在其允许列表里,根 package.json 已经 pin 了 @lydell/node-pty-linux-arm64。只是目前没有任何地方把 QWEN_DESKTOP_TARGET 设成该值,所以这条别名当前不可达。仓库其他地方也已经在构建并发布 ARM64 Linux 产物(audio-capture-prebuilds.yml 与 cd-cua-driver.yml),而 macOS arm64 本来就是本 matrix 的第一行。

评审验证方案

如何验证

关键点是:第二个 Linux AppImage 会让 main 上的脚本报错,而本 PR 让它能无歧义地解析。在 main 上:

$ node .github/scripts/create-desktop-update-manifest.mjs --assets <同时含两个 AppImage 的目录> ...
Error: Expected one updater artifact for linux-x86_64, found 2: Qwen-Code-Desktop_0.24.6_aarch64.AppImage, Qwen-Code-Desktop_0.24.6_amd64.AppImage

$ node .github/scripts/create-electron-bridge-manifest.mjs --assets <同一目录> --platform linux ...
Error: Expected one Electron bridge artifact matching /\.AppImage$/i, found 2: Qwen-Code-Desktop_0.24.6_aarch64.AppImage, Qwen-Code-Desktop_0.24.6_amd64.AppImage

在本分支上两者各自只解析到一个产物,feed 多出 linux-aarch64。

契约测试是纯 Node,不需要安装依赖:

$ cd packages/desktop && node scripts/test-release.js
Desktop release helper checks passed.

其余由 CI 覆盖:任何改动 packages/desktop/、desktop-release.yml 或更新清单脚本的 PR,.github/workflows/ci.yml 都会跑 node scripts/test-release.js。

每一处改动都做了变异测试:逐个回退并确认有测试失败 —— 回退更新清单脚本、删掉 arm64 matrix 行、回退 Electron bridge 脚本、取消 Electron bridge glob 的架构钉死、回退 sync-desktop-to-oss.yml 里的镜像标志、去掉 null 产物的跳过分支、以及把 --allow-missing-platform 从按平台键放宽成整体开关。七项全部失败;全部还原后两套测试均通过。

推理所依赖的两个事实是对照上游源码核实的,不是假设。Tauri 的 appimage.rs 映射 X86_64 => "amd64"、AArch64 => "aarch64",而 debian.rs 映射 AArch64 => "arm64" —— 所以 arm64 的 AppImage 是 _aarch64.AppImage、deb 是 _arm64.deb,两者在合并后的下载目录里都不会与 x64 的文件名冲突。另外 tauri-plugin-updater 的 get_urls 依次查找 ["{os}-{arch}-{installer}", "{os}-{arch}"],其中 updater_os() 返回 linux、updater_arch() 返回 aarch64,因此 linux-aarch64 正是打包后的应用真正会请求的键。

前后对比证据

N/A —— 发布基础设施,没有交互界面。用户可见的结果是发布出来的 feed,本次直接读取了线上 desktop-latest feed 来确认缺口:desktop-v0.24.6 的 desktop-latest.json 恰好只有 darwin-aarch64、darwin-x86_64、windows-x86_64、linux-x86_64,该 release 的 Linux 产物也只有 Qwen-Code-Desktop_0.24.6_amd64.AppImage 与 ..._amd64.deb。

UI 验证:N/A。没有触碰任何应用代码路径 —— 改动只涉及发布 matrix、两个 feed 生成脚本及其测试。所有既有平台的桌面应用二进制与 UI 完全不变;唯一新增的可观测结果是生成的 feed 多一个键、多两个可下载产物,而这两者在真正跑一次发布构建之前都不存在。

测试平台

OS Status
🍏 macOS N/A
🪟 Windows N/A
🐧 Linux ✅

Linux(x86_64 主机):node scripts/test-release.js 通过;vitest run scripts/tests/{desktop-oss-workflow,release-workflow,workflow-size,hosted-process-ci}.test.js 318 通过 / 1 跳过(该跳过是既有的);七个文件的 prettier --check 全部干净;用 scripts/lint.js 里仓库自己的参数跑 actionlint 1.7.12,全部 workflow 干净。macOS 与 Windows 为 N/A —— diff 里没有任何按宿主 OS 分支的逻辑,两个 manifest 脚本也是与架构无关的 Node。

环境(可选)

本机 Node v24.19.0,基于 main 的 worktree。本机没有 Rust 工具链,因此 cargo test(packages/desktop 里的 npm test)与任何真实的 Tauri 打包都无法在本地执行 —— 见下方风险与范围。

风险与范围

  • 主要风险 / 取舍:改动落在发布路径上,出错会影响所有平台的发布,而不只是 arm64。其中风险最高的单点是把 linux-x86_64 的选择从 /\.AppImage$/i 收紧为 /_amd64\.AppImage$/i,即把宽松匹配换成精确匹配。它是 fail-closed 且顺序正确的:manifest 在 Create GitHub release 之前生成,所以 Tauri 改名或 productName 变化会让发布直接中止、不产生任何半成品产物,而不是发出一份架构错误的 feed。
  • 未验证 / 范围外:arm64 构建本身在本机从未执行过 —— 没有 cargo/rustc,桌面发布构建也无法在本地跑起来。静态核实的是它所依赖的各个环节确实存在:AppRun-aarch64、linuxdeploy-aarch64.AppImage、linuxdeploy-plugin-appimage-aarch64.AppImage 上游都有发布,@lydell/node-pty-linux-arm64 已经 pin,Node.js 官方也发布 linux-arm64 包。desktop-packaging-check.yml 每天定时以 dry_run: true 调用本 workflow,所以新腿在真正进入发布之前就会经历一次真实的构建、产物收集与 xvfb 冒烟。注意 dry run 会跳过 publish,因此两个 manifest 脚本是由 test-release.js 的 fixture 覆盖的,而不是由 dry run 覆盖。产出的 AppImage 在 Ubuntu 22.04 arm64 上的实际运行同样未执行。yamllint 本地跑不起来(本机 1.28 无法解析仓库的 .yamllint.yml),该门禁在本地未验证,交给 CI。
  • 破坏性变更 / 迁移说明:对既有平台没有。有一条维护者需要知道的运维事实 —— prepare 的 already_published 探测只比较 .version,与架构无关,因此为了补 arm64 而重新 dispatch 一个已发布版本时,它只会打一条 ::notice:: 然后跳过;那条路径需要 clobber=true。这是既有行为,本 PR 未改动。给历史版本回填 arm64 因此在范围之外;本 PR 让它从下一次发布起存在。

方向评审:跳过 —— 准备本 PR 的运行环境里没有 plan-critic agent。runner 标签的选择(ubuntu-22.04-arm 而非 ubuntu-24.04-arm)改用上游证据定案:它是 GA、硬件规格相同,并且与现有 x64 腿保持 glibc 一致,而不是抬高 arm64 的下限。

关联 Issue

Fixes #12806

ARM64 Linux has no Desktop artifact: the build matrix stops at
x86_64-unknown-linux-gnu, so desktop-latest.json carries four platform
keys and an ARM64 installer that trusts the feed falls back to the
x86_64 AppImage, which cannot execute (#12806).

The matrix row is not the whole change. Both manifest scripts select a
Linux AppImage by extension alone and require exactly one match, so a
second Linux leg breaks publish *after* every build succeeded:

  Expected one updater artifact for linux-x86_64, found 2

The Linux selection is now arch-explicit in create-desktop-update-manifest.mjs
(which gains the linux-aarch64 key), in create-electron-bridge-manifest.mjs,
and in the Electron bridge's release-assets glob. Tauri's AppImage arch
tokens are `_amd64`/`_aarch64` while the .deb beside them uses Debian's
`_amd64`/`_arm64`, so the two Linux legs cannot be told apart by the
Debian token alone.

The arm64 leg builds on ubuntu-22.04-arm rather than ubuntu-24.04-arm so
both Linux artifacts keep one glibc floor (2.35); 24.04 would need 2.39
and drop Ubuntu 22.04 / Debian 12 arm64, which the x64 artifact supports.

Re-mirroring an already-published release through sync-desktop-to-oss.yml
has to reproduce the feed that release actually shipped, so that path
passes --allow-missing-platform linux-aarch64. The fresh-build path stays
strict: a leg that failed to upload must fail the mirror, not be published
as missing.

test-release.js gains a check that the matrix's rust targets and the
manifest's platform keys stay in sync — a leg nobody taught the manifest
about ships silently, which is the failure this issue reports.

Fixes #12806

Co-authored-by: Qwen-Coder <[email protected]>

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 9b920a22. No P0/P1 — review record for a non-author maintainer vote (own PR).

Verified the load-bearing details:

  • Arch tokens are correct: the manifest matches Tauri's real AppImage suffixes (_amd64 / _aarch64), not the Debian ones — and neither pattern can match the other's artifact, so the exactly-one-match invariant holds with both legs present.
  • glibc floor rationale is real: the arm64 leg pins ubuntu-22.04-arm (glibc 2.35, shared with the x64 leg) rather than 24.04 (2.39), which would have produced an artifact that refuses to start on Ubuntu 22.04 / Debian 12 arm64.
  • The --allow-missing-platform escape hatch is scoped exactly right: only the OSS re-mirror-of-an-existing-release path passes it (SOURCE = 'release'), fresh builds stay strict, and the tests pin all three shapes (absent without the flag fails; wrong platform name still fails; right name succeeds and omits the leg).
  • The matrix↔feed cross-check test (testReleaseMatrixCoversUpdaterPlatforms) closes the original #12806 failure mode — a leg nobody taught the manifest about can no longer ship silently.
  • Electron bridge correctly pins its x64-only AppImage selection in both the manifest script and the workflow glob.

CI green at this head (13 pass, 0 fail).

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No confirmed blocking findings.
Approval withheld: arm64 build has never been executed (structural — PR CI cannot run the release matrix).


Scope: All 7 changed files reviewed in full. Not reviewed: arm64 build execution (requires a live release-matrix run; outside reviewer reach by construction).


What I verified:

  1. All three exact-one-match sites updated correctly. On main, three code paths assert exactly one *.AppImage hit. The diff patches all three: (a) create-desktop-update-manifest.mjs switches from /.AppImage$/i to arch-specific /_amd64.AppImage$/i and /_aarch64.AppImage$/i; (b) create-electron-bridge-manifest.mjs switches to /_amd64.AppImage$/i (bridge is x64-only by design); (c) the desktop-release.yml feed-step glob switches to *_amd64.AppImage. The remaining consumers — Collect artifacts (arch-agnostic allow-list), OSS mirror asset checks (existence-only find … -print -quit), sha256sum, and gh release create — are count-agnostic and need no change.

  2. allowMissingPlatforms parsing is safe. When --allow-missing-platform is absent, options['allow-missing-platform'] is undefined, falls back to '', splits to [''], trims, and filter(Boolean) removes the empty string — empty Set, strict mode. No silent opt-out.

  3. The if (!artifact) continue; guard is correct. selectArtifact returns null only when zero matches are found and the platform is in allowMissingPlatforms. The continue skips both the signature check and the manifest entry. The test confirms: --allow-missing-platform linux-aarch64 produces a 4-platform manifest when arm64 artifacts are absent, while --allow-missing-platform darwin-x86_64 still fails on the missing arm64 leg — the escape hatch is per-platform, not a blanket opt-out.

  4. testReleaseMatrixCoversUpdaterPlatforms correctly guards the matrix↔manifest invariant. Parses every rust_target: line in the workflow, maps it through updaterPlatformByRustTarget, and asserts symmetry against every ['platform', entry in the manifest script. This is the sentinel that prevents the silent failure cited in the PR: a leg that builds but is never registered in the feed.

  5. OSS sync re-mirror gate correctly scoped. --allow-missing-platform linux-aarch64 is only injected when $SOURCE = 'release' (re-mirroring an existing GitHub release that predates arm64). Fresh source=artifact builds stay strict.


Non-blocking observations:

  • Re-mirror tolerance has no version floor (sync-desktop-to-oss.yml): The source=release escape hatch is permanent. Re-mirroring a future post-arm64 release whose arm64 leg is genuinely absent would silently publish an incomplete feed instead of failing. In practice publish is strict and cannot produce such a release without clobber=true trickery, but a $VERSION >= X.Y.Z floor would make the escape hatch self-retiring.

  • Test comment slightly overstates the regex guarantee (packages/desktop/scripts/test-release.js): The comment on testReleaseMatrixCoversUpdaterPlatforms says the published regex is keyed on the [platform, selectArtifact( shape "so a commented-out entry stops counting as published." The actual pattern \[\s'((?:darwin|linux|windows)-(?:x86_64|aarch64))'\s*, would still match // ['linux-aarch64',. No impact on correctness; just a slightly weaker guarantee than documented.


Approval blockers: ubuntu-22.04-arm runner availability for this repo, libwebkit2gtk-4.1-dev on jammy/arm64, and Tauri appimage,deb bundle + xvfb smoke success have never been exercised. A workflow_dispatch of desktop-packaging-check.yml on this branch under dry_run: true would settle this before merge.


Cross-check: qwen-code-ci-bot identified the same three non-blocking observations (permanent re-mirror tolerance, test comment regex, unverified build). All three confirmed independently from the diff. No finding in their review that I cannot account for.

Reviewed with AI assistance.

yiliang114 and others added 2 commits September 27, 2026 19:06
All six round-1 suggestions, plus two gaps found while re-checking the
change independently:

- Warn on stdout when --allow-missing-platform actually drops a key. A
  tolerant mirror run was byte-identical in the log to a complete one, so
  a feed published without linux-aarch64 was only discoverable by diffing
  it against the previous mirror -- and the OSS feed is the first updater
  endpoint, so arm64 clients would not fall through to GitHub either.
- Accumulate repeated flags in parseArguments. The new option's two
  spellings disagreed: comma worked, repeated flags silently kept only
  the last value and then failed with an error naming a build leg.
- List create-electron-bridge-manifest.mjs in the desktop_shell
  changed-files filter beside its sibling. test-release.js is the only
  test either feed script has and it runs in that job, so a PR touching
  only the bridge script skipped its own test and reported green. That
  was harmless while one Linux AppImage existed; pinning the selector to
  `_amd64` is what made the filter load-bearing.
- desktop README: the Electron bridge publishes the *x64* Linux AppImage,
  which stopped being unambiguous once a release carries two.
- Tests: pin the manifest_args expansion rather than only its pieces, pin
  that the publish step never tolerates a missing leg, pin that the filter
  lists both scripts, and pin both spellings of the multi-valued option.
- test-release.js: reuse the runManifest helper for the trailing
  invocation it subsumes.

Every new assertion was mutation-tested: dropping the expansion, making
publish tolerant, unlisting the bridge script, reverting the flag
accumulation and removing the warning each turn a suite red.

Co-authored-by: Qwen-Coder <[email protected]>
…ches

The comment said a commented-out entry stops counting as published. That
holds for the multi-line `[platform, selectArtifact(` entries, including
the linux-aarch64 entry this PR adds, but not for a single-line entry
behind a `//` prefix: the pattern is unanchored, so `// ['windows-x86_64',`
still matches and still counts.

Measured both shapes before narrowing the wording rather than the guard:

  comment out the multi-line linux-aarch64 entry
    -> published loses linux-aarch64, assert.deepEqual against the matrix
       fails: "every build matrix leg needs an updater feed entry"
  comment out the single-line windows-x86_64 entry
    -> published unchanged, suite stays green

Tightening the regex to reject a `//`-prefixed single-line entry would add
machinery for an edit to a pre-existing entry this PR does not touch, so
the comment now states the guarantee the code actually gives.

Co-authored-by: Qwen-Coder <[email protected]>
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Scope ledger

Goal: add a linux-aarch64 leg to the Desktop release matrix so desktop-latest.json gains a linux-aarch64 key and the release publishes an arm64 AppImage + deb beside the x86_64 ones (Fixes #12806).

Non-goals: backfilling arm64 into already-published releases; changing any existing platform's artifacts; any app/runtime code path.

Rounds: 1 substantive (cfa6962d, the round-1 review fixes — it changed the manifest script's behaviour, the CI changed-files filter, and the tests). d8ffac7f is a comment-only correction and does not increment the count.

Metrics (vs merge base 1d03a07f)

files +/− production tests docs
baseline 9b920a22 7 +162/−6 5 files, +57/−5 test-release.js +105, desktop-oss-workflow.test.js +7 1 file, +1/−1
round 1 cfa6962d 10 +252/−26 5 files, +66/−6 +184/−18 2 files, +2/−2
round 2 d8ffac7f 10 +252/−26 unchanged 2 lines rewritten in place unchanged

The snapshot script files packages/desktop/scripts/test-release.js under implementation because the name does not match its test glob; it is the only test either feed script has. Split correctly, production code grew by 9 added lines across the round-1 fixes and this round added none.

Scope verdict: corrected. The line-growth trigger fired at round 1 (+34% on the script's own implementation bucket), so this round added no new fixes and took the prescribed subtractive option instead: it narrowed a comment that overclaimed what the parity guard catches, net-zero lines. No suspicious files — all 10 map to the goal or to an accepted finding.

Growth attribution

finding files
R1-1 repeated --allow-missing-platform flags were last-wins create-desktop-update-manifest.mjs, test-release.js
R1-2 a tolerated platform vanished from the feed with no output create-desktop-update-manifest.mjs, test-release.js
R1-3 the CI filter listed one feed script and not its sibling ci.yml, desktop-isolation.test.js
R1-4 the manifest_args expansion itself was unpinned desktop-oss-workflow.test.js
R1-5 runManifest() stopped short of the block it subsumes test-release.js
R1-6 the strict fresh-build side had no assertion desktop-oss-workflow.test.js
reviewer note — README said "Linux AppImage" where the bridge publishes the x64 one packages/desktop/README.md
reviewer note — parity-guard comment overstated the regex test-release.js (round 2)

Round 2's two rewritten lines are the parity-guard comment only; the guard it describes was re-checked by mutation before and after.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Round-2 closeout: no new commit — all six R1 suggestions were already applied by cfa6962d16.

The R1 review was submitted at 2026-09-27T10:12:59Z against 9b920a22 (all three reviews on this PR carry commit_id: 9b920a22bb8c). cfa6962d16 landed at 11:06:20Z, 53 minutes later, and applied every one of the six — four of them verbatim from the reviewer's own suggestion blocks. GitHub re-pointed five of the six thread anchors to the new head, which is why they read as live; R1-2 kept the true 9b920a22 anchor because its line became unanchorable.

Rather than take that on faith, each fix was re-verified by running the reviewer's own witness as a mutation against the current tree, one at a time, restoring between runs:

# Mutation applied Expected Result
R1-1 revert parseArguments to values[name] = value; red rc=1, AssertionError @ create-desktop-update-manifest.mjs:84
R1-2 delete the ::warning:: console.log red rc=1, did not match /::warning::no updater artifact for linux-aarch64/
R1-3 drop create-electron-bridge-manifest\.mjs from ci.yml:2384 red desktop-isolation.test.js 1 failed | 5 passed
R1-4 drop "${manifest_args[@]}" + preceding \ (reviewer's mutation C) red desktop-oss-workflow.test.js 1 failed | 29 passed
R1-4b flip the guard condition 'release' → 'artifact' red 1 failed | 29 passed
R1-6 add --allow-missing-platform linux-aarch64 to publish (reviewer's mutation D) red 1 failed | 29 passed

Unmutated baseline at cfa6962d16: desktop-isolation.test.js + desktop-oss-workflow.test.js = 36 passed (36), node packages/desktop/scripts/test-release.js = passed, worktree clean after all six restores.

R1-5 is a pure dedupe (test-release.js:1516 is now const failure = runManifest();) with no branch a test could pin, as the reviewer itself noted — verified structurally instead: the only remaining manifestScript sites are the two execFileSync calls at :1365/:1404 and the helper at :1436.

One reviewer claim did not survive checking and is corrected in-thread: R1-4 argued the bash-harness replay is "the only form that also catches the if guard being dropped", but desktop-oss-workflow.test.js:341 already toContains the whole guard line including its condition value (mutation R1-4b above). The committed pin therefore covers both the expansion and the guard without adding a second execution harness.

Nothing is deferred this round and nothing is left unresolved. The PR still has reviewDecision: REVIEW_REQUIRED with zero requested reviewers, which is the only thing holding it — requesting review from @chiga0, who already engaged here.

中文:本轮没有新提交——6 条 R1 建议在 cfa6962d16(评审提交后 53 分钟落地)里已经全部修掉,其中 4 条是照 reviewer 给的 suggestion 原样应用。我没有只凭这点下结论,而是把 reviewer 自己的 witness 逐条当变异在当前树上重跑(一次一个、跑完还原),6 个变异全部变红,未变异基线 36 tests 全绿。R1-4 里「只有 bash 回放才钉得住 if 守卫」这句经核实不成立,已在 thread 内更正。目前唯一卡点是 0 个 requested reviewer,已向 @chiga0 发起 review 请求。

@yiliang114
yiliang114 requested a review from chiga0 September 27, 2026 12:30
qqqys
qqqys previously approved these changes Sep 27, 2026

@qqqys qqqys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical-only review at d8ffac7f — approving, with one pre-merge verification named

Base 76c3dc5b. Ten files, +252/-26: the two feed scripts, three workflows, two docs lines, and three test files. I read both manifest scripts, all three workflow diffs, and the call sites that decide whether the tolerance hatch can fire.

Historical blocking issues

None. No CHANGES_REQUESTED has ever been posted on this PR, and of twelve threads all twelve are Suggestions — six resolved, six open. Under a Critical-only scan the open six are not mine to adjudicate.

The two [Critical] labels at this head are not code findings

The bot's review opens with "[Critical] Blocking finding(s) follow" and then says of both items that they are "neither a defect in the diff": one is a triage-stage gate that fires because the PR touches the release pipeline and desktop-latest.json is a contract the shipped updater consumes, and the other records that chiga0's review reports "No confirmed blocking findings" while withholding approval pending verification that the arm64 leg can build. I am treating them as what they are — an unexercised-infrastructure gate — and addressing that below rather than as a code defect.

My scan

The ambiguity the matrix row would otherwise create is closed on every side that selects a Linux artifact. The updater feed's linux-x86_64 row goes from /\.AppImage$/i to /_amd64\.AppImage$/i and a new linux-aarch64 row matches /_aarch64\.AppImage$/i, so with two Linux AppImages present each pattern resolves to exactly one file instead of the matches.length !== 1 throw that an extension-only match would produce. The Electron bridge is pinned the same way twice over: create-electron-bridge-manifest.mjs narrows its linux pattern to /_amd64\.AppImage$/i, and desktop-release.yml's glob becomes release-assets/*_amd64.AppImage while keeping the -ne 1 count check, so the bridge cannot pick up the arm64 artifact or silently accept two. ci.yml's changed-file filter now lists both feed scripts, which is what makes test-release.js — the only test either script has — actually run when the bridge script changes.

The tolerance hatch cannot drop a platform that has an artifact. The guard is conjunctive at head:

if (matches.length === 0 && allowMissingPlatforms.has(platform)) { … return null; }
if (matches.length !== 1) { throw new Error(`Expected one updater artifact for ${platform}, found ${matches.length}…`); }

So the hatch fires only on genuine absence; a present-but-duplicated artifact still throws. The if (!artifact) continue; added to the platform loop sits before the signature check, so a tolerated-null platform does not go looking for null.sig. And the hatch is scoped at the call site rather than in the script: manifest_args=() by default, set to (--allow-missing-platform linux-aarch64) only when [ "$SOURCE" = 'release' ], i.e. only when re-mirroring an already-published release. The fresh-build publish path passes no such flag, so a leg that failed to upload still fails the release instead of being published as missing. That is the right split, and the "${manifest_args[@]}" expansion is present on the invocation.

The new matrix row is the conservative one. ubuntu-22.04-arm rather than 24.04 keeps both Linux artifacts on a single glibc floor of 2.35, so the arm64 build cannot end up requiring 2.39 and refusing to start on the Ubuntu 22.04 and Debian 12 arm64 systems the x64 artifact still supports.

The repeat-accumulation in parseArguments is not reachable with a bad value today. values[name] = values[name] === undefined ? value : \${values[name]},${value}`applies to every option, so a duplicated single-valued flag would comma-join into the signed feed — the concern R1-1's follow-up raises. I checked both invocations: the mirror step passes--assets, --repository, --tag, --version, --base-url, --output` once each plus the optional hatch, and no caller duplicates a flag. It is a latent sharp edge in a shared parser rather than a defect at this head, and correctly filed as a Suggestion.

No Critical found.

The one thing I would verify before merge, and cannot from here

The arm64 leg has never been exercised: ubuntu-22.04-arm availability for this repo, libwebkit2gtk-4.1-dev on jammy/arm64, and a successful Tauri appimage,deb bundle with the xvfb smoke test are all unconfirmed, and this PR's own checks skip the desktop packaging lanes. Nothing in the diff can prove those, and I am not going to pretend otherwise. The reassuring part is the failure direction: if the leg cannot build, the leg fails and publish fails loudly, and the strict fresh-build path means a missing artifact cannot be published as an omitted feed key. A workflow_dispatch of the packaging check on this branch under dry_run: true would settle it, which is what chiga0 asked for and why that review withholds approval. My approval is about the code, not a claim that the build is proven — the maintainer's verification gate still stands and is a reasonable one.

CI

Clean at this head: 13 checks pass, zero failures, nothing pending. The three test files this PR adds or extends — the matrix↔feed parity check in test-release.js, the isolation assertion, and the OSS-workflow assertions — are what carry the invariants above.

…shed

Round-2 review follow-ups on the linux-aarch64 release leg:

- create-desktop-update-manifest.mjs: emit the ::warning::no updater artifact
  annotation only after the feed has been written. It fired at selection time,
  so a run that later threw on a different leg published an annotation claiming
  an incomplete feed went out while no feed existed at all.
- create-desktop-update-manifest.mjs: scope repeat-accumulation to
  --allow-missing-platform, the only genuinely multi-valued option. Accumulating
  for every option comma-joined a duplicated single-valued flag straight into
  the signed feed (version 0.1.0,0.1.0, which no updater client parses as
  semver) or wrote a file named f,f so no feed existed, both at exit status 0.
  Single-valued options keep the ordinary last-wins override.
- test-release.js: pin tolerated-and-present, the normal case on the only
  production caller (the OSS re-mirror path), so dropping the
  matches.length === 0 conjunct cannot silently omit linux-aarch64 from the
  mirror feed; plus the refused-after-tolerating and duplicated-flag cases.
- desktop-isolation.test.js: read the changed-files filter alternatives out of
  the anchored grep -Eq alternation instead of substring-matching the whole run
  script, and pin that the lane the filter gates still runs test-release.js.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmujyzhfka1

Co-authored-by: Qwen-Coder <[email protected]>

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2 re-review at head d8ffac7f, base 76c3dc5b.

Prior-round finding status (round 1 at 9b920a22):

Finding Status
F1-1: Re-mirror tolerance has no version floor (sync-desktop-to-oss.yml) Still stands — --allow-missing-platform linux-aarch64 is permanently available on the source=release path; a future post-arm64 release whose arm64 artifact is genuinely absent would be silently published without it. Non-blocking, no regression in the new range.
F1-2: Test comment regex slightly overclaims its guarantee Fixed at d8ffac7f. Comment now correctly describes the multi-line-entry dependency.

Incremental review (cfa6962d, d8ffac7f):

Two commits since round 1. cfa6962d closes round-1 qwen-code-ci-bot follow-ups; d8ffac7f corrects the comment raised in my prior review. Both are clean: no new logic, no new invariants introduced.

No new blocking findings.

Approval blockers:

Arm64 build execution remains unverified — structural, outside reviewer reach, as noted in round 1. ubuntu-22.04-arm runner availability, libwebkit2gtk-4.1-dev on jammy/arm64, and Tauri appimage,deb + xvfb smoke have not been exercised on this branch. A workflow_dispatch of the desktop-packaging-check workflow under dry_run: true remains the path to settle this before merge.

Cross-check (round 2):

The qwen-code-ci-bot round-2 "Critical" comment flags my round-1 approval blockers as unresolved. Correct — the arm64 execution gap is still open. That is a structural coverage gap, not a code defect in the diff; the code logic reviewed is sound.

Reviewed with AI assistance.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

CI note on the Test (ubuntu-latest, Node 22.x) red that appeared on head 52ea4b5b7c — not caused by this PR, and it cleared on one rerun of the failed jobs (run 36331562886, Test (ubuntu) now completed/success on the same head, no code change).

Evidence for the attribution:

  • The single failing case was packages/cli/src/serve/hosted-harness-session.test.ts → "Hosted Harness no-tool session > omits settled output when only the complete tool result record exceeds the limit", expect(status.body.hasActivePrompt).toBe(false) receiving true (and recoveryBlocked likewise), 3/3 attempts. Everything else passed: 1 failed | 1149 passed (1150) files, 1 failed | 36665 passed | 92 skipped.
  • This PR touches zero files under packages/cli/src/serve/. Its 10 files are .github/scripts/*, .github/workflows/*, docs/design/2026-07-31-desktop-web-shell-release.md, packages/desktop/README.md, packages/desktop/scripts/test-release.js, scripts/tests/desktop-isolation.test.js, scripts/tests/desktop-oss-workflow.test.js. There is no mechanism by which a Desktop release-matrix leg can change a serve session-settling assertion.
  • Head 52ea4b5b7c is Merge branch 'main' at 15:59:36Z. It contains main's 302e7d88ef "feat(serve): add gated Hosted Workspace file tool turns (feat(serve): add gated Hosted Workspace file tool turns #12831)" (15:00:11Z), which is the commit that last touched both hosted-harness-session.ts and hosted-harness-session.test.ts. Runner load at failure time was load[30.13 31.58 24.91] — not the overload band, so this reads as a settle-ordering race in the newly merged feature rather than resource starvation.
  • Cross-check on a sibling PR: feat(agents): add durable workspace collaboration state #12854's Test (ubuntu) red on its own head is a different test (src/agents/workspace-agents/store.test.ts), so this is not one shared root cause across PRs.

Leaving it here rather than patching, since the fix (if the race is real rather than a one-off) belongs to #12831's surface, not to a Desktop release-matrix change. review-pr is still pending on this head.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

…cation

Two test-discrimination gaps raised in round 3 review.

desktop-oss-workflow.test.js pinned the SOURCE env, the guard line, the
tolerant assignment and its expansion separately, but nothing pinned that the
tolerant assignment sits inside the release guard. Hoisting it into the
unconditional declaration leaves every asserted string present and the if
block merely redundant, so a SOURCE=artifact fresh build whose arm64 leg
failed to upload would publish desktop-latest.json without linux-aarch64 and
still exit 0. Assert the guarded block as one contiguous string.

The duplicatedOutput case asserted only status 0, which caught a comma-joined
--output solely because the fixture path is absolute and the write dies with
ENOENT on a parent directory that does not exist. Both production callers
spell --output relatively after a cd (sync-desktop-to-oss.yml:135 and
desktop-release.yml:721), and there the comma-joined value is a legal
filename: the run exits 0, writes desktop-latest.json,desktop-latest.json, and
leaves no feed at the path the upload step checksums. Delete the feed before
the run and read it back the way the sibling duplicatedVersion case does, then
repeat the run with the relative production spelling in its own cwd.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmuk7k4kaah

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 3 re-review at head 95fe885 / base 0a136f89.

Prior-round finding status (round 2 at d8ffac7f, base 76c3dc5b):

Finding Status
F1-1: Re-mirror tolerance has no version floor (sync-desktop-to-oss.yml) Still present, non-blocking — --allow-missing-platform linux-aarch64 is permanently available on the source=release path. Intentional design trade-off per PR description: backfilling arm64 into a historical release is out of scope.

Suggestions addressed since round 2:

All six qwen-code-ci-bot round-1 suggestions have been applied:

  • R1-1 (multi-valued option comma/repeated-flag divergence): fixed in cfa6962d
  • R1-2 (warning annotation absent on tolerant run): fixed in 217ceea5
  • R1-3 (CI changed-file filter missing create-electron-bridge-manifest.mjs): fixed
  • R1-4 (strictness split containment not pinned): fixed in 900863e7
  • R1-5 (runManifest helper coverage): fixed in cfa6962d
  • R1-6 (fresh-build path not covered by strictness test): fixed in 900863e7

Incremental commits reviewed:

217ceea5 — moves the ::warning:: annotation to after the feed is written. The test (doesNotMatch(stdout, /::warning::/) on a refused run) verifies a failed run emits no spurious annotation. Correct. ✓

900863e7 — adds desktop-oss-workflow.test.js tests pinning the strictness split by containment: the whole manifest_args=() if ... then ... fi block must appear in order, and the publish job's manifest step must not contain --allow-missing-platform. These are the critical anti-regression anchors for the whole tolerance mechanism. ✓

Merge commits 52ea4b5b, 95fe885 (base updates from main): no new logic, no conflicts with PR changes. ✓

No new blocking findings.


Approval:

The round-1 and round-2 structural disclosure — arm64 build never executed — is resolved at process level: desktop-packaging-check.yml runs daily with dry_run: true, exercising the new leg (build, artifact-collection, xvfb smoke) before any release runs. Code review confirms the structural pieces are correct: runner label ubuntu-22.04-arm is GA; glibc 2.35 floor is shared with x64; libwebkit2gtk-4.1-dev, AppRun-aarch64, linuxdeploy-aarch64 all exist upstream. Another maintainer (qqqys) reviewed and approved independently. Code review is complete.


Scope: All 10 changed files reviewed across 3 rounds. Incremental range this round: 217ceea5–95fe885 (2 logic commits + 2 merges).

Not covered: arm64 release build execution (inherently outside reviewer reach; desktop-packaging-check.yml daily dry-run is the verification path).

Reviewed with AI assistance.

@qwen-code-review-bot qwen-code-review-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at 95fe8854. I reviewed the original change at 9b920a22 and have now reviewed the four substantive commits since.

The original change holds up. Narrowing the AppImage match from /\.AppImage$/i to /_amd64\.AppImage$/i plus a new /_aarch64\.AppImage$/i is required, not cosmetic — Tauri's AppImage arch tokens are _amd64/_aarch64 while the sibling .deb uses Debian's _amd64/_arm64, so extension-only matching is ambiguous once a second Linux leg exists. ubuntu-22.04-arm rather than 24.04 keeps both Linux legs on a glibc 2.35 floor; an arm64 artifact built on 24.04 would need 2.39 and refuse to start on the Ubuntu 22.04 / Debian 12 arm64 that the x64 artifact still supports. And narrowing the Electron-bridge glob to release-assets/*_amd64.AppImage is the easiest thing to have missed: the existing [ "${#linux_appimages[@]}" -ne 1 ] guard would have failed on two matches and taken the bridge path down with it.

cfa6962d closes the observability gap I raised on the earlier head — a tolerated-missing platform was silently omitted from the feed — and fixes a real CI routing hole beside it: create-electron-bridge-manifest.mjs was absent from the changed-files filter, so editing the bridge manifest never triggered scripts/test-release.js, which is the only test either script has. Emitting the annotation on stdout rather than stderr is correct and the reason is recorded: GitHub parses workflow commands from stdout only, and stderr has to stay clean for the thrown error the tests match on.

217ceea5 corrects two things in its predecessor, both sharply:

  • The annotation moved out of selectArtifact to after fs.writeFileSync(options.output, …), collected via droppedPlatforms. Inside the selector it would also fire on runs that later throw and write nothing, annotating a feed that never existed — the message claims "publishing the feed without it", which is only true once the write has happened.
  • Repeat-flag accumulation was narrowed to allow-missing-platform alone. Accumulating every option would comma-join a duplicated single-valued flag into the published, signed feed: --version a --version a yields a version no updater client can parse, and --output f --output f writes a file named f,f so no feed exists at all — both at exit status 0. I read the final parseArguments at this head and confirmed accumulation is scoped to multiValueOptions with last-wins preserved for everything else.

The test added at 900863e7 and the earlier testReleaseMatrixCoversUpdaterPlatforms are the reason this is safe to land: the matrix↔manifest cross-check is keyed on the [platform, selectArtifact( entry shape, so a commented-out entry stops counting as published, and assert.notEqual(missingLeg.status, 0) keeps the fresh-build path strict. Without that negative control the whole tolerance mechanism could degrade into "never fail" and stay green.

CI is green at this head: Test (ubuntu-latest, Node 22.x), Lint & Static (ubuntu-latest, Node 22.x) and Integration Tests (no-AK, No Sandbox) all passed with nothing failing across the run.

None of the touched paths (.github/scripts/, .github/workflows/ci.yml, packages/desktop/, scripts/tests/, docs/) are covered by CODEOWNERS, so no code-owner approval is required.

@yiliang114
yiliang114 added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 7443411 Sep 28, 2026
58 of 59 checks passed
yiliang114 added a commit that referenced this pull request Sep 28, 2026
Clears the "Check lint gate freshness" step of Lint & Static, which fails
when the lint gate moved on 'main' after this branch last incorporated it:

  .github/workflows/ci.yml: 7443411 feat(desktop): add a
  linux-aarch64 leg to the Desktop release matrix (#12833)

That lane checks out the branch head alone, so its green only proves the
branch passes the gate as the branch defines it. Merging the current gate
in re-validates the branch under it. No PR-authored file is re-touched by
this merge.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmukopes9bd
tonydzi pushed a commit to tonydzi/qwen-code that referenced this pull request Sep 29, 2026
Their Lint & Static lane fails the check-lint-gate-freshness gate: this
branch predates the .github/workflows/ci.yml change that landed on main in
QwenLM#12833 (7443411, 2026-09-28), and that lane checks out the branch head
alone, so it validates against the branch's own stale gate. Merging main
re-validates under the current gate. No source changes.

Assisted-by: Claude (Anthropic) / claude-opus-5
Machine: MacBook-Anton
Account: tonydzi
Operator: Anton Dziatkovskii
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Desktop releases: add linux-aarch64 (AppImage/deb) build to the release matrix

4 participants