Skip to content

fix(scripts): build libghostty-vt for the baseline CPU - #55

Merged
ThomasK33 merged 8 commits into
mainfrom
fix/prebuild-baseline-cpu
Nov 1, 2026
Merged

ThomasK33 merged 8 commits into
mainfrom
fix/prebuild-baseline-cpu

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 1, 2026 •

Copy link
Copy Markdown
Member

Why

coder/agent-tty#179 (native mouse input) needs encodeMouse, which first shipped in beta.2. With beta.4, agent-tty's Linux CI crashes. The linux-x64 prebuild dies with Illegal instruction (SIGILL) on CPUs without AVX-512. That breaks the default libghostty-vt renderer for those users too, and it blocks the agent-tty PR.

Cause

scripts/build-libghostty-vt.sh passed no -Dcpu, so Zig compiled libghostty-vt for the release runner's CPU. The beta.4 addon contains 14,870 AVX-512 (zmm/mask register) instructions. Measured with objdump -d, beta.0 has none. GitHub's x64 runners differ in CPU features, so this repo's own CI passed because its runner had AVX-512 (inferred).

Reproduction with qemu-x86_64 on an AVX-512 host:

Addon -cpu Skylake-Client (AVX2, no AVX-512) -cpu qemu64 (generic x86-64) native
beta.0 prebuild runs not tested runs
beta.4 prebuild exit 132 (SIGILL) exit 132 (SIGILL) runs
this branch, baseline build runs runs runs

What changes

  • build-libghostty-vt.sh passes -Dcpu=$LIBGHOSTTY_VT_CPU (default baseline) and records cpu in ghostty-source.json. A baseline build has no AVX-512. Its only AVX2 code is wuffs' *_x86_avx2 functions, which wuffs selects at run time after a CPUID check.
  • scripts/check-baseline-cpu.sh (mise task check-baseline-cpu) runs examples/smoke.ts in one Node process under qemu-x86_64 -cpu qemu64. It starts the real Node binary (process.execPath), because qemu-user emulates only the binary it starts, and a mise shim would exec Node natively and pass any addon. The linux-x64 job of ci.yml runs it against build/Release. The linux-x64 jobs of publish.yml (the files npm ships) and of the manual release-prebuilds.yml run it against prebuilds/ with PREBUILDS_ONLY=1, before the artifact is uploaded. .gitignore ignores the qemu_*.core dumps (about 700 MB each) that a failing check leaves in the repo root.
  • In CI and the release workflow the check runs with QEMU_CPU=Haswell (AVX2, no AVX-512). On ubuntu-latest, Node without the addon dies with SIGILL under qemu64. The script's control step (node -e 0 under the same CPU, exit 2) showed this in CI. The runner installs Ubuntu's amd64v3 packages, which is the likely cause (inferred). Locally the default qemu64 passes for the baseline build. Under Haswell the baseline build passes and the beta.4 prebuild exits 1.
  • CI installs qemu-user. Ubuntu 26.04 (ubuntu-latest) no longer has qemu-user-static. qemu-user provides /usr/bin/qemu-x86_64 on 24.04 and 26.04.
  • Updated scripts/README.md, the README build section, and the CHANGELOG [Unreleased] entry.
  • ci.yml, release-prebuilds.yml and publish.yml run the macos-arm64 row on macos-15. GitHub cancelled every macos-14 job on this PR during a scheduled brownout, and it retires macos-14 on 2026-11-02 ([macOS] The macOS 14 Sonoma based runner images will begin deprecation on July 6th and will be fully unsupported by November 2nd for GitHub Actions and Azure DevOps actions/runner-images#13518). Without this change the beta.5 release would have no macOS prebuild.

Validation (local, x86_64 Linux, AMD EPYC 9454P)

  • npm run build:libghostty, build:native, build, typecheck, npm test (86/86) and npm run smoke pass.
  • mise run check-baseline-cpu exits 0 on the baseline build, also with QEMU_CPU=Skylake-Client and QEMU_CPU=Nehalem.
  • With beta.4's prebuilds/ copied in and PREBUILDS_ONLY=1, the check exits 1 and prints its error. An earlier draft of the script used command -v node, got the mise shim, and passed beta.4. That is why it now resolves process.execPath.

Follow-up


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

The v0.1.0-beta.4 linux-x64 prebuild crashes with SIGILL on CPUs without
AVX-512. build-libghostty-vt.sh passed no -Dcpu, so Zig compiled for the
release runner's CPU (14,870 AVX-512 instructions in the addon). Under
qemu-user with a Skylake or generic x86-64 CPU, loading it and creating a
terminal dies with exit 132.

libghostty-vt now builds with -Dcpu=$LIBGHOSTTY_VT_CPU (default baseline)
and records the CPU in ghostty-source.json. A baseline build has no
AVX-512; its only AVX2 code is wuffs' runtime-dispatched *_x86_avx2
functions.

scripts/check-baseline-cpu.sh runs examples/smoke.ts in one Node process
under qemu-x86_64 -cpu qemu64. The linux-x64 CI job runs it against
build/Release and the linux-x64 release-prebuilds job against prebuilds/.
It exits 1 for the beta.4 prebuild and 0 for a baseline build.

Change-Id: I21af6fdcb2575f9eededee641c1377a9b96a1c55
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Ie403aa95e2c132f64c7d5d2712e82e2f3f440dc1
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T21:35:59.085984Z e99e5fd Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: fc5c2a8f65

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I4fcd37fee02f3f0d0711a8eb1f8e5185e5aafaea
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Node without the addon dies with SIGILL under qemu64 on ubuntu-latest,
which installs Ubuntu's amd64v3 packages. Haswell has AVX2 and no AVX-512:
the baseline build passes there and the beta.4 prebuild exits 1.

Change-Id: Ibba3fc73e3f99a33feabf21d5e011e060bfb2790
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 1abebc8d4e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

GitHub retires the macos-14 image on 2026-11-02 and cancels macos-14 jobs
during scheduled brownouts (actions/runner-images#13518).

Change-Id: I83ce63b89a76cd4d3c2ab35235edf96b6f539acd
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

Change-Id: I4fca59a13415733dfc6cde9137fa477708786e5e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f599131452

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/release-prebuilds.yml Resolved
Comment thread scripts/build-libghostty-vt.sh Resolved
@ThomasK33

Copy link
Copy Markdown
Member Author

Verdict: PASS+NOTES

Heads: I built and tested 035970330adf0cbb68b465112b95ae76bd111dae. The current head f59913145241a3981a96736c18d6be099ea79b27 differs from it only in CHANGELOG.md (git diff 0359703 f599131 --stat: 1 file changed, 1 insertion, 1 deletion). The fix works on the real artifact. The head linux-x64 addon has no AVX-512 instructions and runs on an emulated CPU without AVX-512. The beta.4 prebuild dies with SIGILL under the same check.

Host: AMD EPYC 9454P with AVX-512, so only the emulated runs count. qemu-user 9.2.1 (Ubuntu 1:9.2.1+ds-1ubuntu5.2 .deb, extracted without root). Node v26.12.1, Zig 0.17.0.

Checks

# Check Measured result
1 npm ci, build:libghostty, build:native, build, typecheck, test, smoke All exit 0. Tests: 6 files and 86 tests passed. vendor/libghostty-vt/ghostty-source.json records "cpu": "baseline".
2a AVX-512 count in the head addon (objdump -d, lines with %zmm or %k0-%k7) build/Release/ghostty_vt_node.node: zmm=0, kmask=0, total 0 of 405408 instructions. libghostty-vt.so.0.1.0: 0 of 332464.
2b Same count on the beta.4 prebuild (npm pack @coder/libghostty-vt-node@0.1.0-beta.4, prebuilds/linux-x64) zmm=12313, kmask=4373, total 14870 of 431656. sha256 db094381…4a24.
2c ymm (AVX2) in the head addon 532 instructions, all inside wuffs *_x86_avx2 functions (wuffs_jpeg__decoder__decode_idct_x86_avx2 and 3 swizzle functions). wuffs picks these at runtime with CPUID. The qemu64 run (no AVX) passes, so the dispatch works.
3a QEMU_CPU=Haswell scripts/check-baseline-cpu.sh on the head build exit 0
3b scripts/check-baseline-cpu.sh (default qemu64) on the head build exit 0
3c Head addon as a prebuild: build:prebuild, verify:prebuilds, then the check with PREBUILDS_ONLY=1, using Haswell and then qemu64 All exit 0. sha256 6fd9dbad…d1d7, the same as 2a.
3d Negative control: beta.4 prebuilds/ in place, PREBUILDS_ONLY=1, Haswell and then qemu64 Both exit 1 with qemu: uncaught target signal 4 (Illegal instruction). The same prebuild runs natively with exit 0, so the addon did load. I restored the tree afterwards.
3e Fail closed No qemu on PATH: exit 2. A qemu that exits 127: exit 2, caught by the node -e 0 control. QEMU_CPU=NoSuchCpu: exit 2.
4a -Dcpu reaches Zig scripts/build-libghostty-vt.sh adds -Dcpu=$CPU to zig_args. The Darwin branch rebuilds the array with "${zig_args[@]:1}", so it keeps the flag. Ghostty reads the flag through b.standardTargetOptions (vendor/ghostty/src/build/Config.zig:95).
4b Workflows actionlint .github/workflows/*.yml: exit 0. CI run 41351631557 on f599131 succeeded: linux-x64, linux-arm64, macos-arm64 (macos-15), and linux-x64-ubsan all passed. The linux-x64 job ran the mise run check-baseline-cpu step.

Notes (none block this PR)

  1. The publish workflow does not run the QEMU check. The prebuilds job in .github/workflows/publish.yml:52 runs mise run release-prebuilds and uploads the result (line 53). The new check runs only in ci.yml and in release-prebuilds.yml, which is a dry run you start by hand. beta.5 will still be built for the baseline CPU, because -Dcpu=baseline is the default in the build script that publish.yml also runs. But nothing emulates the files that actually ship. The CHANGELOG line "CI and the release workflow run the smoke example" can make a reader think the published files are checked. Suggestion: add the same two linux-x64 steps after publish.yml:52. If you do not, run PREBUILDS_ONLY=1 QEMU_CPU=Haswell scripts/check-baseline-cpu.sh against the beta.5 tarball's prebuilds/ before you unblock feat: add mode-aware native mouse input agent-tty#179.
  2. On macOS, the flag does nothing, and that is harmless. vendor/ghostty/src/build/Config.zig:99-103 replaces the target with genericMacOSTarget() when it builds for macOS on macOS. That drops the CPU from the query, so macos-arm64 was already generic. On linux-arm64, baseline means generic ARMv8-A, which all arm64 Linux machines support. CI built and tested it.
  3. CI uses QEMU_CPU=Haswell, so CI catches AVX-512 but not AVX2 or AVX. A later LIBGHOSTTY_VT_CPU=x86_64_v3 build would pass CI. The local qemu64 run (3b, 3c) covers the default build today.
  4. The script does not check that the smoke example produced output. A qemu that runs nothing and exits 0 gives a false pass. I measured this with a fake no-op qemu-x86_64 on PATH: exit 0. This risk is low with the apt qemu-user package. A grep for the smoke example's final output line would close it.
  5. Each failing run writes a qemu core file of about 700 MB to the repo root. I measured 734035968 and 733691904 bytes, saved as qemu_node_<ts>_<pid>.core. .gitignore does not list these files. Running qemu with ulimit -c 0, or ignoring qemu_*.core, prevents an accidental commit.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: high

release-prebuilds.yml is manual-dispatch only. publish.yml builds the files
npm ships, so check them there before upload. Also ignore qemu-user core dumps
that a failing check leaves in the repo root.

Change-Id: I9829884cc978c609a408bb5c593aa91348be86d7
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 6f2477f756

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Ghostty's build replaces a native macOS target with a generic one, so the CPU
override only changes Linux builds.

Change-Id: I9fa9f9db966b53fb26462176e644fa3936310b99
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: e99e5fd6cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
ThomasK33 merged commit d76a9c2 into main Nov 1, 2026
5 checks passed
ThomasK33 added a commit to pmarreck/tui_test that referenced this pull request Nov 1, 2026
beta.4's linux-x64 prebuild was built for an AVX-512 CPU and died with
SIGILL on CPUs without it (coder/libghostty-vt-node#55). beta.5 builds
libghostty-vt for the baseline CPU: 0 AVX-512 instructions, and it runs
under an emulated Haswell and Skylake.
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.

1 participant