Skip to content

L17: receiverHosted lift (fix the amd64 ReleaseSafe SEGV before merge) #264

L17: receiverHosted lift (fix the amd64 ReleaseSafe SEGV before merge)

L17: receiverHosted lift (fix the amd64 ReleaseSafe SEGV before merge) #264

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
# Rapid pushes to a branch (or PR) supersede in-flight runs: cancel the older
# run so the 11-job matrix does not pile up and starve runners.
#
# KNOWN FAILURE MODE (2026-07-30): a run cancelled here can still execute
# setup-zig's post step and save a PARTIAL `.zig-cache`. Because restores are
# prefix-matched, the next run inherits it, and Zig then hits a manifest that
# claims a cached artifact which is not in the tarball:
# error: lld-link: could not open '.zig-cache\o\<hash>\compiler_rt.lib'
# Worse, that run saves its own poisoned cache, so the failure self-perpetuates
# across re-runs and later pushes — a re-run alone never clears it.
# REMEDY: delete the entries and let the next run rebuild clean, e.g.
# gh cache list --json id,key | ... | gh cache delete <id>
# (Windows hit this first because it links compiler_rt.lib explicitly; nothing
# in the repo's own code was involved.)
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
# A hung test binary must fail the job, not burn a runner for the
# 6-hour default.
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
# capnp gates ten serialization/codegen suites, which skip when it is
# missing. Installing it on Linux only meant 26 tests silently skipped on
# macOS and 29 on Windows while docs/stability.md advertised codegen as
# "full" on all three tiers. Linux and macOS now both have it; see the
# Windows note below for why that tier does not.
- name: Install Cap'n Proto compiler (Linux)
if: runner.os == 'Linux'
run: |
if command -v sudo >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y capnproto libcapnp-dev
else
apt-get update
apt-get install -y capnproto libcapnp-dev
fi
- name: Install Cap'n Proto compiler (macOS)
if: runner.os == 'macOS'
run: brew install capnp
# NOT installed on Windows, deliberately. Installing it there (upstream's
# prebuilt tools from the C++ release archive) makes the suites run and
# then FAIL: two of them compile schemas that use an absolute standard
# import (`import "/capnp/stream.capnp"`), which capnp resolves from its
# standard include tree. The Linux apt package ships that tree; the
# Windows archive ships only the .exe files, so those imports cannot
# resolve. Fixing it needs an `-I` include path threaded through ~10 test
# files that currently hardcode their argv — real work, tracked as a known
# gap in docs/stability.md rather than papered over. Windows codegen
# coverage is therefore genuinely absent, not merely unmeasured.
# Turn a missing tool into a hard job failure instead of ~10 test files
# quietly returning SkipZigTest and the suite reporting green. This step
# is the assertion; tests/hardening/toolchain_gate_test.zig covers
# committed fixtures but cannot read the environment to check the tool.
- name: Verify the toolchain the tests depend on is present
if: runner.os != 'Windows'
shell: bash
run: capnp --version
- name: Run tests
timeout-minutes: 20
run: zig build test --summary all
- name: Check compilation
run: zig build check
- name: API snapshot check
# On every OS: the snapshot is target-stable by construction and
# this is the gate that keeps it that way.
run: zig build check-api --summary all
- name: Stable-surface closure check
# A frozen entry point whose signature mentions an Experimental type is
# only nominally frozen. This fails when one does.
run: zig build api-closure --summary all
- name: Vendor RPC schema sync (check-rpc)
if: runner.os == 'Linux'
run: just src/rpc/check-rpc
- name: Generated-artifact drift check
# Linux only (needs the capnp CLI). Regenerates every committed
# generated file + the API snapshot and fails if any drifted — the
# class of bug that turned this workflow red mid-sprint more than once.
if: runner.os == 'Linux'
run: just check-generated
- name: Soak smoke (2s, loopback TCP)
run: zig build soak -- --seconds 2
- name: Self-interop e2e (zig client vs zig server, loopback)
run: zig build e2e-self --summary all
- name: RPC ping-pong example (guards the connection teardown seam)
run: zig build example-rpc
fmt-check:
name: Format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Check formatting
run: just fmt-check
evented-check:
name: Evented backend check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
# This job used to run `zig build -Dio-backend=evented check`, which
# verified nothing that plain `zig build check` did not already.
# `-Dio-backend` is a `[]const u8` compared at RUNTIME by
# `io_backend.parseKind`, so all three arms of `Backend.init` are
# semantically analysed in EVERY configuration. Proven by ablation:
# breaking the `.evented` arm turns plain `zig build check` red with no
# flag passed at all.
#
# What was missing was any lane that EXECUTES a selector. `.threaded` is
# the one that can actually carry RPC today — `std.Io.Evented` has no
# working socket vtable upstream at the pinned toolchain (see
# docs/stability.md), so an evented execution lane is not yet possible.
# Ablation for this one: making `parseKind("threaded")` return null leaves
# the old compile check green and turns this lane red.
- name: Execute the RPC e2e over the threaded Io selector
run: zig build -Dio-backend=threaded e2e-self --summary all
# Kept as a cross-check that the evented selector still *builds* where Zig
# exposes it. Cheap, and it documents the intent of the -Dio-backend
# surface even though `check` subsumes it.
- name: Check evented backend compilation
run: zig build -Dio-backend=evented check --summary all
quic-transport:
name: QUIC baseline/native transport
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
# Package store cached separately from setup-zig's per-run cache; the
# key is shared with nightly.yml's extended-gates job so nightly QUIC
# runs inherit a warm cache from per-push CI.
- name: Restore Zig package cache
uses: actions/cache@v5
with:
path: .zig-cache/p
key: zig-packages-${{ runner.os }}-${{ hashFiles('build.zig.zon') }}
- name: Prefetch dependency tree
# Retries transient upstream failures (googlesource.com 503s) so the
# QUIC lane only fails on real regressions.
run: |
for attempt in 1 2 3; do
if zig build --fetch=all; then
exit 0
fi
echo "::warning::dependency fetch attempt ${attempt} failed"
if [ "$attempt" -lt 3 ]; then
sleep $((attempt * 20))
fi
done
echo "dependency fetch failed after 3 attempts"
exit 1
- name: Build QUIC-enabled targets
run: zig build -Dquic=true --summary all
- name: Check QUIC-enabled build graph
run: zig build -Dquic=true check --summary all
- name: Run QUIC baseline/native transport tests
run: zig build -Dquic=true test-rpc-quic --summary all
- name: Compile QUIC documentation snippet fixtures
run: zig build -Dquic=true test-docs-snippets-quic --summary all
docs-smoke:
name: Docs smoke and snippets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Compile documentation snippet fixtures
run: zig build test-docs-snippets --summary all
- name: Run documentation/examples smoke gate
run: zig build docs-smoke --summary all
- name: Check public API snapshot
run: zig build check-api --summary all
cross-targets:
name: Cross-target compile (${{ matrix.target }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- aarch64-linux-gnu
- x86-linux-gnu # 32-bit
- powerpc64-linux-gnu # big-endian
- x86_64-windows # catches Windows rot without a Windows runner
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Compile-only check for ${{ matrix.target }}
run: zig build check-compile -Dtarget=${{ matrix.target }} --summary all
- name: Compile test suite for ${{ matrix.target }}
# Windows-only: the native Test (windows) job runs the suite for
# real; this step exists so test compile breakage for Windows is
# caught even by contributors without Windows machines.
if: matrix.target == 'x86_64-windows'
run: zig build check-test-compile -Dtarget=${{ matrix.target }} --summary all
hardening:
name: Hardening gate (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Run hardening gate
run: zig build hardening
- name: Run deterministic fuzz/smoke tests
run: zig build test-fuzz-smoke --summary all
- name: Run coverage-guided fuzz targets (deterministic mode)
run: zig build test-fuzz --summary all
- name: Run resource-budget regression tests
run: zig build test-resource-budgets --summary all
- name: Run OOM regression tests
run: zig build test-oom --summary all
- name: Run raw-frame security e2e tests
run: zig build test-e2e-security --summary all
e2e-zig:
name: Zig e2e interop
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Install Cap'n Proto compiler
run: |
if command -v sudo >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y capnproto libcapnp-dev
else
apt-get update
apt-get install -y capnproto libcapnp-dev
fi
- name: Run Zig e2e gate
run: just e2e-zig
# The cross-implementation Level-3 HOSTING proof: the vendored Cap'n Proto
# C++ reference drives the recipient and introducer roles over real TCP
# against a capnp-zig two-peer VatC host.
#
# It is folded into this job rather than given its own because this job
# already pays for docker and the cpp-rpc image, so the marginal cost is
# near zero. Before this step existed, the lane ran in NO workflow and in
# neither `just ci` nor `release-preflight` -- v0.6.0 shipped citing it as
# the evidence for cross-impl hosting while nothing executed it. It is
# also the only gate that would catch a vendored-submodule bump breaking
# conformance.
- name: Cross-impl L3 hosting gate (C++ drives A+B vs a Zig VatC host)
run: just e2e-l3-vatc
bench-check:
name: Benchmark regression check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Run benchmark regression checks
run: zig build -Doptimize=ReleaseFast bench-check
wasm-build:
name: WASM build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Build WASM host
run: zig build wasm-host
release-build:
name: Release build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
- name: Build ReleaseSafe
run: zig build -Doptimize=ReleaseSafe
release-safe-tests:
name: ReleaseSafe hardening tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: jdx/mise-action@v4
with:
version: 2026.7.17
- uses: ./.github/actions/setup-zig
# STILL THE TEN-BINARY SUBSET, and that is a known gap rather than a
# choice. Promoting this to the full suite
# (`zig build test -Doptimize=ReleaseSafe`) is correct and was attempted:
# it passes locally on macOS at 1291/1291, and it immediately found a
# real defect on the other two tiers --
# `rpc_join_readiness_test."L4 Join proxy relay rolls back state under
# OOM injection"` fails under ReleaseSafe on BOTH ubuntu-latest and
# windows-latest, consistently, while passing in Debug everywhere and in
# ReleaseSafe on macOS. That test drives `checkAllAllocationFailures`, so
# the likely shape is an OOM rollback path whose behaviour depends on an
# allocation sequence that optimization changes.
#
# The promotion lands once that is fixed. Recorded here rather than left
# as a silently narrow lane: the subset covers message/codegen/framing/
# fuzz-smoke and two transport files, so most of the RPC runtime -- peer,
# caps, promises, vat, integration -- is still never executed with safety
# checks on in an optimized build.
- name: Run the full suite under ReleaseSafe
run: zig build test -Doptimize=ReleaseSafe --summary all
# ReleaseFast, not for speed but because it is the only mode that does NOT
# poison a freed pointer. A use-after-free reached from a destructor is
# invisible in Debug and ReleaseSafe -- `HostPeer.deinit` freed the queue
# that `Peer.deinit`'s own Finish/Release sends appended to, and all 981
# tests passed anyway until this lane existed.
- name: Run teardown-heavy RPC suites under ReleaseFast
run: zig build test-release-fast --summary all