fix(release): raise prepared package size budget to 112 MiB (#10774) - #10781
Conversation
The prepared package is now 97.18 MiB, 1.18 MiB over the 96 MiB budget, so assertPreparedPackageSize throws and the docker build fails. The E2E sandbox:docker leg builds its image by running this script, so both of its build attempts fail and the step exits 1 on every runner, while the sandbox:none and macOS legs, which never package, stay green. The OpenTUI backend (#10739) added a 1.91 MiB chunk to a bundle that was already within 1.4 MiB of the ceiling. Bump to 112 MiB, the same ~15 MiB of headroom for normal growth that the 85 -> 96 MiB bump in bcf5b7b left.
E2E Report — #10774 · Main CI failed: E2E Tests on
|
| Job | Conclusion | Failed step |
|---|---|---|
| E2E Test (Linux) - sandbox:docker - shard 1/3 | cancelled | exceeded the 60-minute job timeout |
| E2E Test (Linux) - sandbox:docker - shard 2/3 | failure | Run E2E tests |
| E2E Test (Linux) - sandbox:docker - shard 3/3 | failure | Run E2E tests |
| E2E Test (Linux) - sandbox:none - shards 1–3 | success | — |
| E2E Test - macOS - shards 1–2 | success | — |
Run logs are admin-only, so the diagnosis used the public jobs and check-run annotations endpoints. Every red docker job — across this run, the preceding push run at ec7caa2399 (33587037818) and the nightly at b26403154b (33590493339) — carries the same two annotations:
[warning] sandbox image build failed; retrying once
[failure] Process completed with exit code 1.
The last green run (33579624662 at 165a03596a) has zero annotations on its docker jobs. Nine different pool runners (hk3-2/9/30, hk4-12/13, hk5-2/4/18/22/26) fail identically, so this is deterministic, not host-specific. The failure is still live at current main: in run 33592836087 (7df5ac6898) docker shard 1/3 had already failed while every sandbox:none and macOS leg passed.
One detail pins the failure to the second build attempt rather than to the tests. The gap between the warning line and the failure line is 805 log lines in shard 2/3 (922 → 1727) and 805 in shard 3/3 (230 → 1035) of the reported run — independent runners producing byte-identical output volume. A vitest shard run cannot do that; a deterministic build failure can. Under the step's set -euo pipefail, the retried build_image failing is what exits the step.
Root cause
npm run prepare:package throws. Reproduced locally on a tree built from the failing commit:
Error: Prepared package unpacked size 101897981 bytes exceeds 100663296 bytes
at assertPreparedPackageSize (scripts/prepare-package.js:401:9)
at preparePackage (scripts/prepare-package.js:52:3)
97.18 MiB against a 96 MiB budget — over by 1,234,685 bytes (1.18 MiB).
Why this breaks only the docker leg: the Dockerfile's builder stage is the only place in main's push CI that runs prepare:package:
RUN QWEN_SKIP_PREPARE=1 npm ci && npm run build && npm run bundle \
&& npm run prepare:package && cd dist && npm packe2e.yml's host steps stop at npm run bundle, and ci.yml never packages at all (verified by grep — no prepare:package, npm pack, or build:sandbox in it). So the throw surfaces exclusively as a sandbox-image build failure, exactly matching the observed job matrix. The chain is: prepare:package throws → docker build exits non-zero → the workflow's one retry throws again → set -e ends the step → shard 1/3, whose first attempt died early, burned its remaining budget and hit timeout-minutes: 60.
What pushed it over: feat(opentui): Activate the OpenTUI backend behind QWEN_TUI_RENDERER (#10739) added dist/chunks/start-opentui-ui-DXDSHGTW.js at 2,002,450 bytes. The arithmetic lines up with the last green run:
101,897,981 − 2,002,450 = 99,895,531 bytes = 95.27 MiB → under the 96 MiB budget
The bundle was already within 1.4 MiB of the ceiling before that commit landed, so any feature-sized addition would have tipped it. The growth is legitimate — no source maps ship (0 *.map files in dist), and the large constituents are intentional: vendor/ripgrep 25 MB across five platforms, web-shell/assets 20 MB, chunks 51 MB.
The fix
Raise the budget from 96 MiB to 112 MiB, leaving 14.82 MiB of headroom over today's 97.18 MiB — enough for roughly eight OpenTUI-sized additions, while still catching a re-introduced heavy dependency, which is what the gate was written for.
This follows existing precedent exactly. bcf5b7bfdd — fix(release): raise prepared package size limit to 96 MB (#6687) (#6691) — fixed the identical failure mode ("causing the Docker sandbox build to fail") by raising this same constant in these same two files, 80 → 85 → 96 MiB. Shrinking the package instead was considered and rejected as out of scope: trimming ripgrep platforms would break cross-platform installs, and cutting web-shell syntax grammars or bundle chunks is a product decision, not a CI repair.
The pinning test was updated in lockstep, mirroring the precedent's shape: title, the pass threshold (111 MiB), the throw threshold (112 MiB), and the exact byte value in the assertion (117440512).
Verification
Each command actually run, with its result:
npm run build— passed (exit 0)npm run typecheck— passed (exit 0)npm run lint— passed (exit 0;eslint . --ext .ts,.tsx && eslint integration-tests)npx eslint scripts/prepare-package.js scripts/tests/package-assets.test.js— passed (exit 0)npx prettier --check scripts/prepare-package.js scripts/tests/package-assets.test.js— passed (no findings)npm run prepare:packagebefore the fix — failed (exit 1) withPrepared package unpacked size 101897981 bytes exceeds 100663296 bytes— the reproduced root causenpm run bundleafter the fix — passed (exit 0)npm run prepare:packageafter the fix — passed (exit 0),✅ Package prepared for publishing at dist/npm packindist/— passed (exit 0), producedqwen-code-qwen-code-0.22.3.tgz(26,962,709 bytes)npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/package-assets.test.js— 34 passednpm run test:scripts(full suite) — 2070 passed, 4 failed; all 4 investigated below, none attributable to this change- Prepared-size recomputation against the new budget —
unpacked=101897981 bytes (97.18 MiB), budget=117440512 (112 MiB), headroom=14.82 MiB → PASS git status --shortafter commit — clean; only the two intended files are in the commit
Mutation probe
The changed guard was probed to confirm the updated test really pins it:
- Reverted only the constant to
96 * 1024 * 1024, leaving the test at 112 MiB → the test FAILED as required:Error: Prepared package unpacked size 116407194 bytes exceeds 100663296 bytesat the.not.toThrow()assertion. - Restored the constant to
112 * 1024 * 1024→ 34 passed.
So the budget value is genuinely witnessed; the suite does not stay green with the guard removed.
The 4 full-suite test:scripts failures — all cleared
None are in a file this change touches, and package-assets.test.js passes. To avoid guessing, the same four files were run in isolation twice: once with the change reverted to pristine HEAD, once with it applied.
| Test file | Full suite (with change) | Isolated, change reverted | Isolated, change applied |
|---|---|---|---|
lint.test.js |
failed (17898 ms) | passed (2930 ms) | passed (3321 ms) |
acp-serve-boundary-guard.test.js |
failed (36988 ms) | passed (10951 ms) | passed (4858 ms) |
qwen-autofix-workflow.test.js |
failed (293717 ms) | passed (282014 ms) | passed (215357 ms) |
verify-capture.test.js |
failed | failed (same test) | failed (same test) |
The first three pass in isolation both with and without the change, and ran 3–6× slower in the full parallel suite — resource-contention flakes, not logic failures. verify-capture.test.js > renders 256-colour and truecolor via the default-grey fallback fails identically with the change reverted, so it is pre-existing in this environment (terminal colour rendering; this run has no TTY). It is unrelated to packaging and is not touched here.
Environment limits, stated plainly
Two commands failed for sandbox reasons and were re-run successfully, so neither is evidence about the repo:
npm ciin a clean/tmpexport of the failing commit aborted with massTAR_ENTRY_ERROR ENOENT(exit 243) — this agent's sandbox filesystem, not a lockfile problem. The in-containernpm ciis separately proven healthy by CI: the hostInstall dependencies,Build project, andBundle CLIsteps all succeeded on every docker job, so the failure is downstream atprepare:package.npm packfirst exited 243 onEACCES mkdir /home/github-runner/.npm. Re-run with a writableHOME/npm cache it exited 0 and produced the tarball.
Not verifiable here: the docker build itself and the image's runtime stage (npm install -g /tmp/*.tgz) — no docker daemon is reachable from this sandbox (docker info fails). The workflow's own CI is the final gate for those. Note the runtime stage sits after the step that was throwing, and the deterministic 805-line second-attempt signature places the failure inside the builder stage.
Also worth flagging for maintainers, found in passing and not changed here: packages/qwen-live/package.json declares @agentclientprotocol/sdk: ^0.14.1 but package-lock.json's packages["packages/qwen-live"].dependencies entry does not list it (the package is present hoisted at node_modules/@agentclientprotocol/sdk@0.14.1, which is why npm ci still succeeds). Out of scope for this CI repair.
中文说明
E2E 报告 — #10774 · main 分支 CI 失败:E2E Tests(提交 b26403154b70)
结论
已定位根因、本地复现、修复并验证。autofix/issue-10774 分支上一个提交:8978bf42c8 — fix(release): raise prepared package size budget to 112 MiB (#10774),改动 2 个文件,新增 7 行、删除 5 行。
实际失败的是什么
被报告的运行(33588890820)只在 sandbox:docker 这一条腿上失败:
| 任务 | 结论 | 失败步骤 |
|---|---|---|
| E2E Test (Linux) - sandbox:docker - shard 1/3 | cancelled | 超过 60 分钟任务超时 |
| E2E Test (Linux) - sandbox:docker - shard 2/3 | failure | Run E2E tests |
| E2E Test (Linux) - sandbox:docker - shard 3/3 | failure | Run E2E tests |
| E2E Test (Linux) - sandbox:none - shard 1–3 | success | — |
| E2E Test - macOS - shard 1–2 | success | — |
运行日志需要 admin 权限,因此诊断改用公开的 jobs 与 check-run annotations 接口。所有失败的 docker 任务——包括本次运行、前一次 ec7caa2399 的 push 运行(33587037818)以及 b26403154b 的 nightly 运行(33590493339)——都带着完全相同的两条标注:
[warning] sandbox image build failed; retrying once
[failure] Process completed with exit code 1.
而最近一次绿色运行(33579624662,提交 165a03596a)的 docker 任务标注为零条。九个不同的池运行机(hk3-2/9/30、hk4-12/13、hk5-2/4/18/22/26)失败表现完全一致,说明这是确定性失败,而非单机问题。该失败在当前 main 上仍然存在:运行 33592836087(7df5ac6898)中 docker shard 1/3 已经失败,而所有 sandbox:none 与 macOS 腿全部通过。
有一个细节可以把失败定位到第二次构建尝试,而不是测试本身。在被报告的运行里,warning 行与 failure 行之间的间隔在 shard 2/3 是 805 行(922 → 1727),在 shard 3/3 也是 805 行(230 → 1035)——不同运行机产出了字节级一致的输出量。vitest 分片运行不可能做到这一点,而确定性的构建失败可以。在该步骤的 set -euo pipefail 下,正是重试的 build_image 失败导致步骤退出。
根因
npm run prepare:package 抛错。在基于失败提交构建的本地树上已复现:
Error: Prepared package unpacked size 101897981 bytes exceeds 100663296 bytes
at assertPreparedPackageSize (scripts/prepare-package.js:401:9)
at preparePackage (scripts/prepare-package.js:52:3)
97.18 MiB 对上 96 MiB 的预算——超出 1,234,685 字节(1.18 MiB)。
为什么只有 docker 腿会挂: 在 main 的 push CI 中,Dockerfile 的 builder 阶段是唯一会执行 prepare:package 的地方:
RUN QWEN_SKIP_PREPARE=1 npm ci && npm run build && npm run bundle \
&& npm run prepare:package && cd dist && npm packe2e.yml 的宿主机步骤到 npm run bundle 就停了,而 ci.yml 完全不做打包(已用 grep 确认:其中没有 prepare:package、npm pack 或 build:sandbox)。所以这个抛错只会表现为沙箱镜像构建失败,与观察到的任务矩阵完全吻合。整条链路是:prepare:package 抛错 → docker build 非零退出 → 工作流的那一次重试再次抛错 → set -e 结束该步骤 → shard 1/3 因为第一次尝试很早就失败,耗尽了剩余预算并撞上 timeout-minutes: 60。
是什么把它推过线的: feat(opentui): Activate the OpenTUI backend behind QWEN_TUI_RENDERER (#10739) 新增了 dist/chunks/start-opentui-ui-DXDSHGTW.js,大小 2,002,450 字节。算术与最近一次绿色运行对得上:
101,897,981 − 2,002,450 = 99,895,531 字节 = 95.27 MiB → 低于 96 MiB 预算
在该提交落地之前,打包体积距离上限已经只剩 1.4 MiB,因此任何一个特性级别的增量都会把它推过去。这次增长是合理的——没有 sourcemap 被打包(dist 中 *.map 文件为 0 个),主要构成都是有意为之:vendor/ripgrep 五个平台共 25 MB、web-shell/assets 20 MB、chunks 51 MB。
修复方案
把预算从 96 MiB 提升到 112 MiB,相对当前的 97.18 MiB 留出 14.82 MiB 余量——大约可以容纳八个 OpenTUI 体量的新增,同时仍然能拦住被重新引入的重型依赖,而那正是这道闸门设立的初衷。
这完全沿用了既有先例。bcf5b7bfdd — fix(release): raise prepared package size limit to 96 MB (#6687) (#6691) — 修的是完全相同的失败形态("causing the Docker sandbox build to fail"),做法就是在这同样两个文件里提升同一个常量,80 → 85 → 96 MiB。也曾考虑改为缩减包体积,但因超出范围而否决:裁掉 ripgrep 平台会破坏跨平台安装,而削减 web-shell 语法高亮文件或 bundle chunk 属于产品决策,不是 CI 修复。
固定该常量的测试也同步更新,形态与先例一致:标题、通过阈值(111 MiB)、抛错阈值(112 MiB),以及断言中的精确字节数(117440512)。
验证
实际执行过的每条命令及其结果:
npm run build— 通过(exit 0)npm run typecheck— 通过(exit 0)npm run lint— 通过(exit 0;eslint . --ext .ts,.tsx && eslint integration-tests)npx eslint scripts/prepare-package.js scripts/tests/package-assets.test.js— 通过(exit 0)npx prettier --check scripts/prepare-package.js scripts/tests/package-assets.test.js— 通过(无问题)- 修复前的
npm run prepare:package— 失败(exit 1),报错Prepared package unpacked size 101897981 bytes exceeds 100663296 bytes,即复现出的根因 - 修复后的
npm run bundle— 通过(exit 0) - 修复后的
npm run prepare:package— 通过(exit 0),输出✅ Package prepared for publishing at dist/ - 在
dist/中执行npm pack— 通过(exit 0),产出qwen-code-qwen-code-0.22.3.tgz(26,962,709 字节) npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/package-assets.test.js— 34 项通过npm run test:scripts(全量)— 2070 通过、4 失败;4 项均已在下文排查,无一可归因于本次改动- 按新预算重新计算打包体积 —
unpacked=101897981 bytes (97.18 MiB), budget=117440512 (112 MiB), headroom=14.82 MiB → PASS - 提交后的
git status --short— 干净;提交中只包含预期的两个文件
变异探针(mutation probe)
对改动的守卫做了探针验证,确认更新后的测试确实钉住了它:
- 只把常量回退为
96 * 1024 * 1024、测试保持 112 MiB → 测试按要求失败:在.not.toThrow()断言处报Error: Prepared package unpacked size 116407194 bytes exceeds 100663296 bytes。 - 把常量恢复为
112 * 1024 * 1024→ 34 项通过。
也就是说该预算值确实被测试见证;把守卫去掉后测试套件不会仍然是绿的。
全量 test:scripts 中的 4 个失败——已全部排除
它们都不在本次改动涉及的文件里,且 package-assets.test.js 是通过的。为避免臆测,同样这四个文件被单独隔离跑了两轮:一轮把改动回退到干净的 HEAD,一轮带上改动。
| 测试文件 | 全量套件(带改动) | 隔离运行(回退改动) | 隔离运行(带改动) |
|---|---|---|---|
lint.test.js |
失败(17898 ms) | 通过(2930 ms) | 通过(3321 ms) |
acp-serve-boundary-guard.test.js |
失败(36988 ms) | 通过(10951 ms) | 通过(4858 ms) |
qwen-autofix-workflow.test.js |
失败(293717 ms) | 通过(282014 ms) | 通过(215357 ms) |
verify-capture.test.js |
失败 | 失败(同一个用例) | 失败(同一个用例) |
前三个在隔离运行时无论带不带改动都通过,且在全量并行套件中慢 3–6 倍——属于资源争用导致的抖动,不是逻辑失败。verify-capture.test.js > renders 256-colour and truecolor via the default-grey fallback 在回退改动后同样失败,因此它是本环境中已存在的问题(终端色彩渲染;本次运行没有 TTY)。它与打包无关,本次未做改动。
环境限制,如实说明
有两条命令因沙箱原因失败,随后都重跑成功,因此二者都不能作为关于仓库本身的证据:
- 在失败提交的干净
/tmp导出树中执行npm ci,出现大量TAR_ENTRY_ERROR ENOENT并中止(exit 243)——这是本 agent 的沙箱文件系统问题,不是 lockfile 问题。容器内的npm ci另有 CI 证据证明其健康:每个 docker 任务上的宿主机Install dependencies、Build project、Bundle CLI步骤都成功了,所以失败发生在下游的prepare:package。 npm pack首次以 exit 243 失败,原因是EACCES mkdir /home/github-runner/.npm。改用可写的HOME/npm 缓存后重跑,exit 0 并产出了 tarball。
此处无法验证的部分:docker build 本身,以及镜像的 runtime 阶段(npm install -g /tmp/*.tgz)——本沙箱无法访问 docker 守护进程(docker info 失败)。这些由工作流自身的 CI 做最终把关。需要说明的是 runtime 阶段位于抛错步骤之后,而那个确定性的 805 行第二次尝试特征把失败定位在 builder 阶段内。
另外有一处顺带发现、供维护者参考,本次未改动:packages/qwen-live/package.json 声明了 @agentclientprotocol/sdk: ^0.14.1,但 package-lock.json 中 packages["packages/qwen-live"].dependencies 条目并未列出它(该包以提升方式存在于 node_modules/@agentclientprotocol/sdk@0.14.1,这也是 npm ci 仍能成功的原因)。这超出本次 CI 修复的范围。
🧠 Handled by Qwen Code · model/模型 qwen3.8-max-2026-09-02
|
Thanks for the PR! Template looks good ✓ Problem: observed failure, verified independently — not theoretical. Direction: aligned, with precedent — #6688 (85 MiB) and #6687/#6691 (96 MiB) made the same bump in the same two files under the same Size: 7 additions / 5 deletions across 2 files under Approach: minimal and the right shape — bump the constant, update the pinning test in lockstep, and add a comment explaining the non-obvious coupling (the docker E2E leg runs this script inside the Dockerfile builder stage, which is why a release gate surfaces as a CI failure). No drive-by changes. Risk: no elevated risk signals — none of the changed files match the revert-correlated paths. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的故障,且已独立核实——不是理论问题。 方向:对齐,且有先例——#6688(85 MiB)与 #6687/#6691(96 MiB)以相同的 规模:2 个文件,7 行新增 / 5 行删除,均在 方案:最小且形态正确——提升常量、同步更新固定该值的测试,并加注释说明不那么明显的耦合(docker E2E 腿在 Dockerfile 的 builder 阶段运行本脚本,这正是发布闸门表现为 CI 失败的原因)。无顺手改动。 风险:无升级风险信号——改动文件均不匹配与 revert 相关的高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewIndependent proposal before reading the diff: a deterministic size gate firing at 101,897,869 > 100,663,296 bytes calls for raising No blockers. What I checked:
TestingUnattended CI run — no PR code was built or executed here. Evidence carried: the PR's own CI checks via the API, plus my independent read of the failing At review time the two substantive legs are still running; the finalize job updates the table once CI settles:
Two things to know about this evidence:
Sandboxed verification would settle this: 中文说明代码审查读 diff 之前的独立方案:一个在 101,897,869 > 100,663,296 字节处触发的确定性体积闸门,对应的修法就是把 无阻塞项。核查内容:
测试无人值守 CI 运行——此处未构建或执行任何 PR 代码。所携证据:通过 API 获取的 PR 自身 CI 检查,以及我对 评论时两条实质性腿仍在运行;finalize 任务会在 CI 落定后更新表格(见上方英文表格)。要点有二:
沙箱验证可以一锤定音: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 5/5 — independently reproduced failure, minimal fix, test pins the new boundary on both sides, established precedent. This one is as clean as it gets. The problem is not the PR's framing — I pulled the two most recent failing The approach matches what I would have done, and it follows the repo's own precedent (#6688, #6687/#6691 made this exact bump). The one real tradeoff — 16 MiB of headroom means a 2–15 MiB regression would now pass the gate — is stated honestly in the PR body; the gate is a tripwire for re-introduced heavy dependencies (tens of MiB), not a regression detector, and the alternative is leaving The only thing no pre-merge evidence can show is the packaging step actually passing on a real tree — E2E is push-only by design — but the arithmetic on CI-measured sizes (101.9 MB measured vs 117.4 MB budget) is airtight, and the finalize job will hold the approval if the still-running Ubuntu leg (which executes the pinned test) lands red. Approval deferred until CI lands green on 中文说明Confidence: 5/5 —— 独立复现的故障、最小修复、测试双向固定新边界、有既定先例。 这是最干净的一类。问题不是 PR 的一面之词——我亲自调取了 方案与我的独立提议一致,且遵循仓库自身先例(#6688、#6687/#6691 做过完全相同的提升)。唯一真实的取舍——16 MiB 余量意味着 2–15 MiB 级别的回退现在会被闸门放过——已在 PR 正文中如实说明;这道闸门是拦截重型依赖(数十 MiB 量级)重新引入的绊线,不是回退检测器,而替代方案是让 合并前唯一无法展示的证据,是打包步骤在真实代码树上确实通过——E2E 按设计仅 push 触发——但基于 CI 实测体积的算术(实测 101.9 MB 对预算 117.4 MB)无懈可击;仍在运行的 Ubuntu 腿(执行边界测试的那条)若变红,finalize 任务会扣下批准。 批准将推迟到 CI 在被审提交上全绿后执行——执行边界测试的 Ubuntu 单元测试腿仍在运行中。 — Qwen Code · qwen3.8-max Reviewed at |
#10781 raised the budget to 112 MiB on main while this branch was open, with the same constant and the same test values this branch carried. Take main's version of prepare-package.js wholesale — the budget half of this branch is now upstream, and what remains is the diagnosability half: build_sandbox.js surfacing the container build's output, and quality_build running prepare:package before the Dockerfile does.
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
|
Released in v0.23.0. |
What this PR does
Raises the published package's unpacked-size budget from 96 MiB to 112 MiB, and updates the test that pins that default in lockstep.
Why it's needed
main's E2E Tests workflow has been red in itssandbox:dockerleg since the OpenTUI backend landed, and it is still red on the current tip. The docker leg builds its sandbox image by running the repo's own packaging step, and that step now throws because the prepared package has grown to 97.18 MiB — 1.18 MiB past the ceiling. Both build attempts fail, the step exits non-zero on every runner, and one shard burns its remaining job budget and times out. Thesandbox:noneand macOS legs stay green because they never package: the Dockerfile's builder stage is the only place inmain's push CI that runs this check, which is why a release gate presented as a CI failure and why the docker-only job matrix looked like an infrastructure problem.The growth is legitimate. The OpenTUI backend added a single 1.91 MiB chunk to a bundle that was already within 1.4 MiB of the ceiling, so any feature-sized addition would have tipped it. Nothing accidental is shipping: no source maps are in the package, and the large constituents are the five-platform ripgrep binaries, the web-shell assets, and the code-split chunks.
Raising the budget is the established response here. An earlier commit fixed this exact failure mode — the Docker sandbox build dying on an over-budget package — the same way, in the same two files, moving 80 → 85 → 96 MiB. 112 MiB restores roughly the same headroom that bump left, enough for normal growth between releases while still catching a re-introduced heavy dependency, which is what the gate exists for. Shrinking the package instead was considered and rejected: trimming ripgrep platforms would break cross-platform installs, and cutting web-shell grammars or bundle chunks is a product decision rather than a CI repair.
Reviewer Test Plan
How to verify
Confirm the packaging step goes from throwing to passing on a current tree:
Before, it fails with
Error: Prepared package unpacked size 101897981 bytes exceeds 100663296 bytes. After, it prints✅ Package prepared for publishing at dist/. Continuing intocd dist && npm packproduces the tarball normally.Confirm the budget is still genuinely enforced rather than merely loosened:
34 tests pass. The pinning test asserts 111 MiB is accepted and 112 MiB throws with the exact byte value
117440512. As a mutation probe, reverting only the constant to96 * 1024 * 1024while leaving the test at 112 MiB makes that test fail withPrepared package unpacked size 116407194 bytes exceeds 100663296 bytes— so the value is witnessed, and the suite does not stay green with the guard removed.The end-to-end confirmation is the workflow itself: on merge, the E2E Tests
sandbox:dockershards should get past the sandbox-image build instead of reportingsandbox image build failed; retrying oncefollowed by a non-zero exit.Evidence (Before & After)
N/A — non-UI change. The before/after is the packaging command's exit status and error text, quoted above.
Tested on
Linux only. The prepared size is platform-independent — the package ships every platform's ripgrep binary and the chunks are JS — so the measurement holds for the container build, which is also linux/amd64.
Environment (optional)
No docker daemon was reachable from the verification sandbox, so
docker builditself and the image's runtime stage (npm install -gof the packed tarball) were not exercised locally; CI is the gate for those. Everything up to and includingnpm packwas run for real. Note the runtime stage sits after the step that was throwing.Two commands initially failed for sandbox reasons and were re-run successfully, so neither is evidence about the repo:
npm packfirst hitEACCES mkdir ~/.npmand passed with a writable npm cache, and a clean-treenpm ciaborted on the sandbox filesystem rather than on any lockfile problem.Risk & Scope
main. A re-introduced heavy dependency, the failure the gate was written for, is tens of MiB and still trips it.packages/qwen-live/package.jsondeclares@agentclientprotocol/sdkwhile the lockfile's entry for that workspace does not list it.npm cistill succeeds because the package is present hoisted, so this is latent drift rather than a break, and it is unrelated to this failure.Linked Issues
Fixes #10774
中文说明
这个 PR 做了什么
把已发布包的解包体积预算从 96 MiB 提升到 112 MiB,并同步更新固定该默认值的测试。
为什么需要
自 OpenTUI 后端落地以来,
main的 E2E Tests 工作流在sandbox:docker这条腿上一路是红的,并且在当前最新提交上仍然是红的。docker 腿通过运行仓库自身的打包步骤来构建沙箱镜像,而该步骤现在会抛错,因为准备好的包已经增长到 97.18 MiB——超出上限 1.18 MiB。两次构建尝试都失败,步骤在每台运行机上都以非零退出,其中一个分片耗尽剩余任务预算并超时。sandbox:none与 macOS 腿保持绿色,因为它们从不做打包:在main的 push CI 中,Dockerfile 的 builder 阶段是唯一运行这项检查的地方——这正是一道发布闸门表现为 CI 失败的原因,也是为什么"只有 docker 失败"的任务矩阵看起来像基础设施问题。这次增长是合理的。OpenTUI 后端只新增了一个 1.91 MiB 的 chunk,而打包体积此前距离上限已只剩 1.4 MiB,因此任何特性级别的增量都会把它推过去。没有意外内容被打包进去:包里没有 sourcemap,主要构成是五个平台的 ripgrep 二进制、web-shell 静态资源,以及代码分割产生的 chunk。
提升预算是这里既有的处理方式。此前有一个提交修的就是完全相同的失败形态——Docker 沙箱构建因包超预算而死——用的就是同样的办法、改的同样是这两个文件,把 80 → 85 → 96 MiB 一路调上来。112 MiB 恢复了那次提升所留下的差不多同等余量,足以容纳两次发布之间的正常增长,同时仍然能拦住被重新引入的重型依赖,而那正是这道闸门存在的意义。也曾考虑改为缩减包体积,但被否决:裁掉 ripgrep 平台会破坏跨平台安装,而削减 web-shell 语法文件或 bundle chunk 属于产品决策,不是 CI 修复。
审阅者测试计划
如何验证
在当前代码树上确认打包步骤从抛错变为通过:
之前它会以
Error: Prepared package unpacked size 101897981 bytes exceeds 100663296 bytes失败;之后它会输出✅ Package prepared for publishing at dist/。继续执行cd dist && npm pack可以正常产出 tarball。确认该预算仍然是被真正强制执行的,而不只是被放松了:
34 项测试通过。固定该值的测试断言 111 MiB 被接受、112 MiB 抛错,并带上精确字节数
117440512。作为变异探针,只把常量回退为96 * 1024 * 1024、测试保持 112 MiB,会让该测试以Prepared package unpacked size 116407194 bytes exceeds 100663296 bytes失败——所以这个值是被见证的,把守卫去掉后测试套件不会仍然是绿的。端到端的确认来自工作流本身:合并后,E2E Tests 的
sandbox:docker分片应当能够越过沙箱镜像构建,而不再报sandbox image build failed; retrying once后以非零码退出。证据(改动前与改动后)
N/A——非 UI 改动。改动前后的差异就是上面引用的打包命令退出状态与错误文本。
测试环境
仅 Linux。打包体积与平台无关——包里已经带了每个平台的 ripgrep 二进制,chunk 也都是 JS——所以这个测量对容器构建同样成立,而容器构建也是 linux/amd64。
环境(可选)
验证所用的沙箱无法访问 docker 守护进程,因此
docker build本身以及镜像的 runtime 阶段(对打好的 tarball 执行npm install -g)没有在本地跑过;这些由 CI 把关。直到npm pack为止的所有步骤都是真实执行过的。需要注意 runtime 阶段位于抛错步骤之后。有两条命令最初因沙箱原因失败,随后都重跑成功,因此二者都不能作为关于仓库本身的证据:
npm pack首次遇到EACCES mkdir ~/.npm,在改用可写的 npm 缓存后通过;干净树上的npm ci是中止在沙箱文件系统上,而不是任何 lockfile 问题上。风险与范围
main上每一个 docker E2E 分片。而这道闸门本来要防的"重新引入重型依赖",量级在数十 MiB,仍然会触发它。packages/qwen-live/package.json声明了@agentclientprotocol/sdk,而 lockfile 中该 workspace 的条目并未列出它。因为该包以提升(hoisted)方式存在,npm ci仍然成功,所以这是潜在的漂移而不是故障,且与本次失败无关。关联 Issue
Fixes #10774