* Reduce native dependency installs to the host platform * Remove install policy documentation * Guard cross-arch packaging and scope release installs to the runner electron-builder only logs a warning for a missing extraResources source, so a host-only install silently shipped a foreign-arch slice without its natives — `pnpm build:mac` on Apple Silicon produced an x64 DMG with no sherpa-onnx-darwin-x64 and no @parcel/watcher-darwin-x64. The previous beforePack hook covered only win32. - Add assertPackagedNativeVariantsInstalled, an arch-aware check over the target's sherpa-onnx, @parcel/watcher, and (on Windows) node-gyp addons. beforePack now runs it for every platform, with remedies split: another architecture comes from install:release, the os:win32 addons need a Windows host. - Drop --os from the release installs. Every packaging job already runs on a runner whose OS matches its target, so only the macOS lanes need extra breadth, and only on CPU for their x64+arm64 config. Windows and Linux packaging return to a plain host-only install. - Add --frozen-lockfile to install:release so a bare run cannot rewrite the lockfile. - Restore the install policy reference doc and the CONTRIBUTING note, plus the rationale comments dropped from the runtime contract test. - Gate the packaging-closure assertions on whether the Windows addons are installed rather than on the host OS, so a cross-arch install exercises them off Windows too. - Make the workflow contract test read `run:` steps as well as retry-action commands, and enforce host-only scoping on the non-macOS packaging lanes. - Remove the unreferenced install measurement script; its numbers live in the policy doc. * Track the install policy doc and index it from AGENTS.md docs/** is ignored behind a per-file allow-list, so the new reference doc was only committed via git add -f and future edits would be skipped. Add it to the allow-list and give it an AGENTS.md entry like every other tracked reference doc, so the host-only install rule is discoverable before someone packages a second architecture. * Route Windows-lane removals through the retrying helper Adding these four specs to the PR Windows lane pulled them into the windows-lane-tree-removal-boundary ratchet, which failed on 20 raw recursive removals. On Windows a bare rmSync races a handle the OS has not released, throwing EPERM after the assertions already passed and reporting a green test as a lane failure. * Adapt the packaging guard to the vendored Windows registry addon main vendored windows-native-registry as the workspace package @orca/windows-registry (#20438). A workspace link resolves on every host, so including it in the installed-Windows-addons checks proved nothing. @vscode/windows-process-tree is the only os: win32 npm addon left, so it alone decides whether the win32 resource plan resolves.
5.2 KiB
Native dependency install policy
Ordinary pnpm install installs optional native dependencies for the current OS
and CPU only. This applies to local development and root-project CI jobs,
including jobs using .github/actions/install-node-dependencies. Mobile and
cloud projects with their own workspace configuration are separate.
The one cross-target build in the repo is macOS: pnpm build:mac and the four
macOS packaging workflows produce both x64 and arm64 artifacts from an arm64
runner. Before packaging for another architecture, widen the CPU set:
pnpm install:release
This runs pnpm install --frozen-lockfile --cpu=current,x64,arm64. It never
widens the OS set: every packaging job runs on a runner whose OS matches its
target, so cross-OS installs are never needed. The macOS workflows pass
--cpu=current,x64,arm64 directly; Windows and Linux packaging jobs use a plain
host-only pnpm install --frozen-lockfile. Keeping current in the list
preserves the host's build tools alongside the target resources. An install for
another target does not itself cross-compile native addons.
Packaging guard
electron-builder only logs a warning for a missing extraResources source and
continues, so without a check a foreign-architecture slice would ship silently
broken. beforePack in
config/electron-builder.config.cjs
therefore calls assertPackagedNativeVariantsInstalled in
config/packaged-runtime-node-modules.cjs,
which fails the build when the target platform/architecture's native variants
are not installed: sherpa-onnx-*, @parcel/watcher-*, and on Windows the
node-gyp addon @vscode/windows-process-tree. The error names every missing
package and gives the remedy that fits: another architecture's variants come
from pnpm install:release, the Windows addon does not (see below).
Windows packaging requires a Windows host. @vscode/windows-process-tree is an
os: win32 npm addon, so it is installed only where that matches;
@orca/windows-registry is a workspace package that links on every host, but
its native binary is still compiled only on Windows. Both are compiled only by
the Windows-only rebuild in config/scripts/rebuild-native-deps.mjs
(allowBuilds in pnpm-workspace.yaml keeps pnpm itself from running node-gyp
for them). The guard checks @vscode/windows-process-tree alone because the
workspace link is present everywhere and proves nothing. pnpm install:release
does not help on macOS or Linux because it does not widen the OS set.
Tests that inspect installed Windows addons and their packaging closure run on
Windows, where those dependencies are required. The PR Windows lane explicitly
includes them. Loading the packaging config tolerates absent Windows addons; only
beforePack rejects Windows packaging until they are installed. Patch-source
assertions, fixture tests, and the isolated real patch-install test continue to
run on other hosts.
Existing checkouts
A narrowing incremental install can leave previously installed variants behind.
Stop development processes using this checkout, remove this checkout's
node_modules, then run pnpm install --frozen-lockfile to obtain a fresh host
install. Do not use --force: pnpm 12 documents that it installs optional
packages even when their OS/CPU/libc do not match. The shared pnpm download store
is separate; this change does not clear it.
Measurement: macOS arm64, pnpm 12.0.0
Measured 2026-09-12 with the same package manifest, lockfile, patch files, and
existing download store in two fresh install directories, both with
--frozen-lockfile --ignore-scripts --offline. One used the earlier broad
policy (--os=current,darwin,linux,win32 --cpu=current,x64,arm64); the other
used --os=current --cpu=current. Numbers are logical file bytes in the virtual
store, not unique disk usage: pnpm hardlinks and APFS clones may share
storage.
| Measure | Broad install | Host install | Reduction |
|---|---|---|---|
| Package directories | 1,296 | 1,206 | 90 |
| Regular files | 53,972 | 52,915 | 1,057 |
| Logical package bytes | 2,485,153,773 | 1,159,396,649 | 1,325,757,124 (53.3%) |
| Logical package GiB | 2.31 | 1.08 | 1.23 |
| Install wall time, single warm-cache trial | 11.98 s | 11.25 s | 0.73 s |
| Native family | Broad MiB | Host MiB |
|---|---|---|
| Canvas | 235.7 | 25.8 |
| Sherpa speech | 235.2 | 71.6 |
| oxlint and tsgolint | 234.0 | 32.7 |
| SWC | 225.8 | 24.4 |
| TypeScript | 158.4 | 26.2 |
Electron downloads and native rebuild outputs are absent from both trials. The single warm-cache timing pair does not establish a speedup, and Linux/Windows results were not measured; do not present the macOS numbers as CI savings.