agentleFS
yao-pkgyao-pkg/pkg/.claude/skills/pkg-xcompile-test/SKILL.md

pkg-xcompile-test

Skill930 starsChanged 6 months agoMITView on GitHub

Cross-compile test harness for @yao-pkg/pkg. Builds a tiny hello.js for every (mode × target) combination and runs what can be executed on a Linux host (native x64, arm64 via docker+qemu, win-x64 via docker-wine). Use when the user wants to verify pkg cross-compilation claims, reproduce issues #87/#181, sanity-check a pkg PR, or compare Standard vs Enhanced SEA across Node 20/22/24. Trigger: "test cross-compile", "run pkg matrix", "verify xcompile", or /pkg-xcompile-test.

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Run from the repo root (paths in the examples are relative to it).

    line 81
  2. Very specific:

    Not verified — see "Host requirements" above.

    line 176
  3. Very specific:

    Do not reuse cached binaries between matrix runs — delete bin-node${major}/ first if you rebuilt pkg.

    line 231
  4. Very specific:

    If you add a new target to the matrix, also add a runner branch in run_one().

    line 234
  5. Very specific:

    If scottyhardy/docker-wine becomes unavailable, tobix/wine is a smaller drop-in replacement — it has the same EBADF stdout gotcha.

    line 236
  6. Very specific:

    Don't confuse "builds with warning" with "builds clean".

    line 238

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

pkg cross-compile test harness

Validates pkg cross-OS and cross-arch support across Node 20/22/24 and Standard vs Enhanced SEA modes. Produces a consistent result table so claims in docs and issues can be checked against reality.

When to use

  • User is about to edit docs about cross-compile support and needs ground truth.
  • User is looking at a cross-compile issue (e.g. #87, #181) and wants to reproduce it.
  • User bumped pkg-fetch or touched bootstrap/prelude and wants a smoke test across targets.
  • User wants to know whether a regression is Node-version specific.

Host requirements

This harness is designed for a Linux x86_64 host with:

  • nvm with node 20, 22, and 24 installed (nvm install 20 22 24).
  • docker daemon running.
  • docker image scottyhardy/docker-wine (pulled on first win-x64 run).
  • tonistiigi/binfmt installed for cross-arch containers — if you see exec format error on arm64 runs, install with:
    docker run --privileged --rm tonistiigi/binfmt --install arm64
  • pkg built from this repo (yarn build) so lib-es5/bin.js exists. The script resolves the pkg entry from its own location — it lives at .claude/skills/pkg-xcompile-test/run-matrix.sh and defaults PKG_BIN to <repo-root>/lib-es5/bin.js.

Not supported: macOS runtime verification. docker-osx needs /dev/kvm and darling is dead. macOS regressions have to be caught on a real Mac or on the GitHub Actions macos-* runners — note that in the final report.

What it runs

For each Node major (20, 22, 24) the harness:

  1. Uses nvm use $major so the pkg host node matches the target node major. This matters — SEA's blob generator uses host execPath when host-major == target-major, otherwise tries to run the downloaded target-arch node binary (which fails without cross-arch emulation).
  2. Loops over modes × targets:
    • Modes: std, std-public (--public-packages "*" --public), sea
    • Targets: linux-x64, linux-arm64, win-x64, macos-x64, macos-arm64
  3. Records build result (OK / FAIL), then tries to run the binary:
    • linux-x64 → native
    • linux-arm64 → docker run --platform linux/arm64 ubuntu:latest
    • win-x64 → docker run scottyhardy/docker-wine (see gotcha below)
    • macos-* → skipped (SKIP-no-mac)
  4. Prints a summary table.

Wine gotcha

wine inside a non-tty docker container produces invalid stdio file descriptors, causing Node to crash with Error: open EBADF before any user code runs. The workaround is to redirect wine's stdout/stderr to files inside the container, then cat them back:

docker run --rm -v "$BINDIR:/mnt" scottyhardy/docker-wine \
  bash -c "wine '/mnt/app.exe' </dev/null >/tmp/out 2>/tmp/err; cat /tmp/out"

Without this, every wine run will look like a pkg failure when it isn't.

Usage

Run from the repo root (paths in the examples are relative to it).

Quick run (single node version)

# Runs the full matrix for node 22 targets with node 22 as host
./.claude/skills/pkg-xcompile-test/run-matrix.sh 22

Full sweep (20 + 22 + 24)

for V in 20 22 24; do
  ./.claude/skills/pkg-xcompile-test/run-matrix.sh $V
done

Custom pkg build

./.claude/skills/pkg-xcompile-test/run-matrix.sh 22 /path/to/other/pkg/lib-es5/bin.js

Custom work directory

By default build outputs go to /tmp/pkg-xcompile/bin-node<major>/. Override with:

PKG_XCOMPILE_WORKDIR=/somewhere/else ./.claude/skills/pkg-xcompile-test/run-matrix.sh 22

Reading the output

Each cell is BUILD / RUN:

  • OK / OK — works
  • OK / FAIL — built, but runtime error on target
  • FAIL / n/a — build failed
  • OK / SKIP-no-mac — can't test macOS runtime on this host

Known results (captured 2026-04-15)

Host: Ubuntu 24.04 x86_64, pkg HEAD of docs/github-pages-site. Tests run with matching host-node / target-node major.

Node 20

targetstdstd-publicsea
linux-x64OK / OKOK / OKFAIL — SEA needs host ≥ 22
linux-arm64OK / OKOK / OKFAIL (same)
win-x64OK / OKOK / OKFAIL (same)
macos-x64OK / n/aOK / n/aFAIL (same)
macos-arm64OK / n/aOK / n/aFAIL (same)

Standard cross-compile on Node 20 works without workarounds. SEA mode is unavailable because pkg enforces host node ≥ 22.

Node 22

targetstdstd-publicsea
linux-x64OK / OKOK / OKOK / OK
linux-arm64OK / FAILOK / OKOK / OK
win-x64OK / FAILOK / OKOK / OK
macos-x64OK / n/aOK / n/aOK / n/a
macos-arm64OK / n/aOK / n/aOK / n/a

Standard cross-compile on Node 22 is broken:

  • linux-arm64 crashes at runtime with Error: UNEXPECTED-20 in readFileFromSnapshot. Matches the #181 failure mode.
  • win-x64 exits silently with EXIT=4 and no stdout. Matches the #87 Windows silent-exit bug.

Both are fixed by adding --public-packages "*" --public (which skips the V8 bytecode step). Enhanced SEA avoids both out of the box.

Node 24

targetstdstd-publicsea
linux-x64OK / OKOK / OKOK / OK
linux-arm64OK / OKOK / OKOK / OK
win-x64OK / OKOK / OKOK / OK
macos-x64OK / n/aOK / n/aOK / n/a
macos-arm64OK / n/aOK / n/aOK / n/a

Node 24 works out of the box for Standard and SEA, same as Node 20. The Node 22 bug is a Node-22-specific pkg-fetch patch regression, not a permanent Standard-mode limitation.

macOS runtime

Not verified — see "Host requirements" above. The regressions tracked in #181 (macOS host, Node 22+) must be confirmed on a real Mac. GitHub Actions' macos-13 / macos-14 runners are the right place for that; a follow-up CI workflow that runs the hello.js matrix there would close the last hole.

Debugging a failing run

Each cell writes its own log; paths are printed on stderr next to any FAIL and re-summarised at the end of the run.

  • Log location — $PKG_XCOMPILE_WORKDIR/logs/node<major>/build-<mode>-<target>.log (and run-<mode>-<target>.log). Default workdir is /tmp/pkg-xcompile. Logs are per-cell so a failing build is not overwritten by the next one.
  • Re-run one cell — the fastest way is still to invoke pkg directly against the same fixture:
    cd /tmp/pkg-xcompile
    node /path/to/pkg/lib-es5/bin.js hello.js -t node22-linux-arm64 \
      -o /tmp/pkg-xcompile/bin-node22/std-linux-arm64
  • exec format error on arm64 runs — binfmt is not registered. After a reboot tonistiigi/binfmt needs to be re-installed:
    docker run --privileged --rm tonistiigi/binfmt --install arm64
  • Wine cell always FAIL — pull the image manually once (docker pull scottyhardy/docker-wine) and inspect logs/node<major>/run-sea-win-x64.log for the actual crash. The EBADF-stdout gotcha is documented above.
  • SEA FAIL on Node 20 — expected; pkg enforces host node ≥ 22 for SEA.
  • Error: UNEXPECTED-20 / silent exit (EXIT=4) — these are the tracked Node-22 Standard-mode regressions (issues #181 and #87). Not environment bugs — see Node 22 results below.
  • Stale binary hiding a regression — pkg does not hash its own source into the output. After rebuilding pkg, delete bin-node<major>/ before re-running the matrix.

The script

The harness lives next to this SKILL.md as run-matrix.sh. Before first run, set PKG_BIN to your pkg build and ensure nvm is sourced.

Quick reference of pkg flags touched

FlagEffect
-t nodeNN-<os>-<arch>Target triple
--seaEnhanced SEA mode
--public-packages "*" --publicDisable V8 bytecode, include sources in plaintext — the cross-compile escape hatch for Standard mode
--no-bytecodeSame effect for bytecode, but does not imply --public — use with --public-packages if you want consistent behavior
--debugInject diagnostic bootstrap; enables DEBUG_PKG at runtime

Notes for future maintainers

  • Do not reuse cached binaries between matrix runs — delete bin-node${major}/ first if you rebuilt pkg. pkg does not hash its own source into the output, so stale binaries silently hide regressions.
  • If you add a new target to the matrix, also add a runner branch in run_one(). Unknown targets fall through the case and count as pass.
  • If scottyhardy/docker-wine becomes unavailable, tobix/wine is a smaller drop-in replacement — it has the same EBADF stdout gotcha.
  • Don't confuse "builds with warning" with "builds clean". The matrix only records OK when the binary file exists on disk; a warning like Failed to make bytecode node22-arm64 still produces a binary but it will crash at runtime. Runtime is the source of truth.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.