ci: give aarch64-darwin a runtime gate (suite + frontend smoke) #225

Open
natsukium wants to merge 10 commits from ci/darwin-smoke into main
Owner

Gives aarch64-darwin both halves of a runtime gate (#110), and fixes what the first runs of that gate found.

The jobs

  • darwin-testnix develop -c cargo nextest run --workspace --all-features --no-fail-fast, the command pr.yml
    runs, on the real target. --no-fail-fast because a first red run on a target that has never run the suite should
    name every failure, not the one that finished first.
  • darwin-smoke — mirrors linux-smoke: needs: darwin-build (the second nix build resolves to the store path
    the build job produced), a per-run socket name, -- cat so the marker reaches the screen through tty echo, and a
    pkill cleanup. It also needs: darwin-test, as windows-smoke does.

The smoke cannot pass yet. macOS has no equivalent of the Linux job's Xvfb + lavapipe, so the macos runner has to
serve a logged-in Aqua session; it currently runs as a launchd daemon under a service account (HOME is
/var/lib/forgejo-runner/default), and the run confirms it: session attach and daemon autospawn both succeed, the OS
clipboard reports "not supported with the current system configuration", and no frame ever paints. This is the same
requirement windows.yml has for DX12, and it is a host-configuration change, not a code one.

What the suite found on macOS, and the fixes

The first full run was 3343 passed / 9 failed / 1 timed out. Two were real bugs in the bridge, the rest were tests
betting on timing or on a Linux-only syscall behavior.

Product fixes (both user-visible, both in the CHANGELOG):

  • An id was freed one write too late. cli.md promises an id stays in use "until the request's reply or stream
    terminal is published as a complete stdout line". The writer retired it after the delimiter, so a client reusing
    the id the instant it read the terminal could be refused malformed_request for an id the contract had already freed.
  • A lost daemon could exit 0. The shutdown select had no precedence, so a client closing stdin after reading a
    daemon_lost terminal left both branches ready and tokio's random pick decided the exit code. Biased now, failures
    first.

Test fixes:

  • sync() in the bridge harness replaces the 300–400 ms sleeps before daemon.kill(). The bridge dials before reading
    its first line, so a written request proved neither the dial nor the read; a probe round trip proves both.
    run_golden's kill now has no timer at all. (Reproduced on Linux: deleting the sleep gives the identical panic.)
  • a_daemon_write_failure_... used shutdown(SHUT_RD) on the peer, which is local to the caller on BSD/macOS — the
    write succeeded there and the test hung to nextest's 180 s ceiling. It now drops the peer, and is renamed for what it
    actually pins.
  • Seven vttest_smoke scenarios failed at exactly their 3 s draw budget. wait_for polls and returns on the first hit,
    so the budget is now 30 s: free on success, tolerant on a busy machine.

Still open: felis-daemon agent::tests::a_racing_teardown_never_strands_a_live_registration fails on macOS with
readlink returning EINVAL — the path was not a symlink — which the code's staged-symlink-plus-rename should make
impossible. Not reproducible on Linux (0 failures in 30 loaded runs), so this commit adds the path's actual type to the
failure message and the next macOS run says what it was.

No publish job. A claim moves on a target's first green run, not on the gate existing.

Gives `aarch64-darwin` both halves of a runtime gate (#110), and fixes what the first runs of that gate found. ## The jobs - **`darwin-test`** — `nix develop -c cargo nextest run --workspace --all-features --no-fail-fast`, the command `pr.yml` runs, on the real target. `--no-fail-fast` because a first red run on a target that has never run the suite should name every failure, not the one that finished first. - **`darwin-smoke`** — mirrors `linux-smoke`: `needs: darwin-build` (the second `nix build` resolves to the store path the build job produced), a per-run socket name, `-- cat` so the marker reaches the screen through tty echo, and a `pkill` cleanup. It also `needs: darwin-test`, as `windows-smoke` does. **The smoke cannot pass yet.** macOS has no equivalent of the Linux job's Xvfb + lavapipe, so the `macos` runner has to serve a logged-in Aqua session; it currently runs as a launchd daemon under a service account (`HOME` is `/var/lib/forgejo-runner/default`), and the run confirms it: session attach and daemon autospawn both succeed, the OS clipboard reports "not supported with the current system configuration", and no frame ever paints. This is the same requirement `windows.yml` has for DX12, and it is a host-configuration change, not a code one. ## What the suite found on macOS, and the fixes The first full run was 3343 passed / 9 failed / 1 timed out. Two were real bugs in the bridge, the rest were tests betting on timing or on a Linux-only syscall behavior. **Product fixes** (both user-visible, both in the CHANGELOG): - *An id was freed one write too late.* `cli.md` promises an id stays in use "until the request's reply or stream terminal is published as a complete stdout line". The writer retired it **after** the delimiter, so a client reusing the id the instant it read the terminal could be refused `malformed_request` for an id the contract had already freed. - *A lost daemon could exit `0`.* The shutdown select had no precedence, so a client closing stdin after reading a `daemon_lost` terminal left both branches ready and tokio's random pick decided the exit code. Biased now, failures first. **Test fixes:** - `sync()` in the bridge harness replaces the 300–400 ms sleeps before `daemon.kill()`. The bridge dials before reading its first line, so a written request proved neither the dial nor the read; a probe round trip proves both. `run_golden`'s kill now has no timer at all. (Reproduced on Linux: deleting the sleep gives the identical panic.) - `a_daemon_write_failure_...` used `shutdown(SHUT_RD)` on the peer, which is local to the caller on BSD/macOS — the write succeeded there and the test hung to nextest's 180 s ceiling. It now drops the peer, and is renamed for what it actually pins. - Seven `vttest_smoke` scenarios failed at exactly their 3 s draw budget. `wait_for` polls and returns on the first hit, so the budget is now 30 s: free on success, tolerant on a busy machine. **Still open:** `felis-daemon agent::tests::a_racing_teardown_never_strands_a_live_registration` fails on macOS with `readlink` returning EINVAL — the path was not a symlink — which the code's staged-symlink-plus-rename should make impossible. Not reproducible on Linux (0 failures in 30 loaded runs), so this commit adds the path's actual type to the failure message and the next macOS run says what it was. **No publish job.** A claim moves on a target's first green run, not on the gate existing.
ci: run the frontend smoke on the macOS runner
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 1m59s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 50s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m4s
pr / nix flake check (pull_request) Successful in 20s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m45s
pr / wire schema is compatible with the base (pull_request) Successful in 8s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 53s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
windows / cargo nextest (Windows) (pull_request) Successful in 3m59s
windows / frontend smoke (Windows) (pull_request) Successful in 1m37s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
darwin / frontend smoke (aarch64-darwin) (pull_request) Has been cancelled
5c7f3ed1fc
aarch64-darwin was the one committed target with a runner but no runtime
evidence: `nix build .#felis` proves it compiles, and says nothing about
whether a winit window, a Metal surface, and a painted frame exist there.
The smoke harness is already platform-neutral (`FELIS_SMOKE_MARKER` on
felis-client), so the gap was a job, not code.

No headless variant: macOS has no equivalent of the Linux job's Xvfb and
lavapipe, so the runner has to serve a logged-in Aqua session, as the
Windows job already requires a real desktop session for DX12.

This is half a gate. The Cargo suite still has no macOS job, so darwin.yml
keeps publishing nothing (issue #110).

Assisted-by: Claude Code:Opus 5
ci: run the Cargo suite on the macOS runner
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m7s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 51s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m4s
pr / nix flake check (pull_request) Successful in 21s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m42s
pr / wire schema is compatible with the base (pull_request) Successful in 12s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 52s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 13s
windows / cargo nextest (Windows) (pull_request) Successful in 3m45s
windows / frontend smoke (Windows) (pull_request) Successful in 1m37s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
darwin / cargo nextest (aarch64-darwin) (pull_request) Failing after 6m42s
darwin / frontend smoke (aarch64-darwin) (pull_request) Has been skipped
e33cc7729e
The smoke covers the window and GPU half; without the suite beside it
aarch64-darwin still had no gate, so the target could never earn a claim
no matter how many green smokes it accumulated. The runner already has
Nix, so the suite is the same command pr.yml runs, on the real target.

The smoke now waits on it, as the Windows chain does: a GUI slot spent
proving a frame paints is wasted while the suite is red.

Assisted-by: Claude Code:Opus 5
natsukium changed title from ci: run the frontend smoke on the macOS runner to ci: give aarch64-darwin a runtime gate (suite + frontend smoke) 2026-09-08 09:06:25 +09:00
The bridge dials the daemon before it reads its first line, so writing
requests into its stdin proves nothing about whether it ever connected.
The test killed the daemon 400 ms after the writes, which is a bet on
process startup: lose it and the bridge dials a dead socket, emits one
cold-socket error, and exits, so the two replies the test collects never
arrive and stdout hits EOF instead.

macOS lost that bet on the runner's first, coldest test. Awaiting a reply
before the kill is what the sibling outstanding-operations test already
does, and it removes the timer from the equation.

Assisted-by: Claude Code:Opus 5
ci: let the macOS suite report every failure
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m5s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m4s
pr / nix flake check (pull_request) Successful in 35s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m39s
pr / wire schema is compatible with the base (pull_request) Successful in 10s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 51s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
windows / cargo nextest (Windows) (pull_request) Successful in 3m56s
windows / frontend smoke (Windows) (pull_request) Successful in 1m40s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
darwin / cargo nextest (aarch64-darwin) (pull_request) Failing after 14m53s
darwin / frontend smoke (aarch64-darwin) (pull_request) Has been skipped
00201ff51c
A first red run on a target that has never run the suite should name all
of its failures, not the one that happened to finish first: fail-fast
turns each fix into another full round trip on the runner.

Assisted-by: Claude Code:Opus 5
natsukium force-pushed ci/darwin-smoke from 00201ff51c
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m5s
darwin / detect relevant changes (pull_request) Successful in 3s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 52s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m4s
pr / nix flake check (pull_request) Successful in 35s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m39s
pr / wire schema is compatible with the base (pull_request) Successful in 10s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 51s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 14s
windows / cargo nextest (Windows) (pull_request) Successful in 3m56s
windows / frontend smoke (Windows) (pull_request) Successful in 1m40s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
darwin / cargo nextest (aarch64-darwin) (pull_request) Failing after 14m53s
darwin / frontend smoke (aarch64-darwin) (pull_request) Has been skipped
to be2132c127
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m29s
darwin / detect relevant changes (pull_request) Successful in 4s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 50s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m53s
pr / nix flake check (pull_request) Successful in 42s
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m48s
pr / wire schema is compatible with the base (pull_request) Successful in 14s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m36s
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 3s
windows / windows docs-only no-op (pull_request) Has been skipped
windows / cargo clippy (Windows cross) (pull_request) Successful in 21s
windows / cargo nextest (Windows) (pull_request) Successful in 5m52s
windows / frontend smoke (Windows) (pull_request) Successful in 2m28s
darwin / cargo nextest (aarch64-darwin) (pull_request) Failing after 14m55s
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
darwin / frontend smoke (aarch64-darwin) (pull_request) Has been skipped
2026-09-09 01:34:17 +09:00
Compare
Some checks failed
bench / Criterion full-suite snapshot (pull_request) Has been skipped
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
bench / Criterion regression gate (pull_request) Successful in 2m29s
darwin / detect relevant changes (pull_request) Successful in 4s
Required
Details
darwin / darwin docs-only no-op (pull_request) Has been skipped
Required
Details
darwin / build felis (aarch64-darwin) (pull_request) Successful in 50s
Required
Details
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 1m53s
pr / nix flake check (pull_request) Successful in 42s
Required
Details
pr / cargo build / clippy / test / deny (pull_request) Successful in 2m48s
Required
Details
pr / wire schema is compatible with the base (pull_request) Successful in 14s
Required
Details
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 1m36s
Required
Details
pr / publish felis (x86_64-linux) (pull_request) Has been skipped
Required
Details
windows / detect relevant changes (pull_request) Successful in 3s
Required
Details
windows / windows docs-only no-op (pull_request) Has been skipped
Required
Details
windows / cargo clippy (Windows cross) (pull_request) Successful in 21s
Required
Details
windows / cargo nextest (Windows) (pull_request) Successful in 5m52s
Required
Details
windows / frontend smoke (Windows) (pull_request) Successful in 2m28s
Required
Details
darwin / cargo nextest (aarch64-darwin) (pull_request) Failing after 14m55s
Required
Details
windows / package felis (x86_64-pc-windows-msvc) (pull_request) Has been skipped
Required
Details
darwin / frontend smoke (aarch64-darwin) (pull_request) Has been skipped
Required
Details
This pull request has changes conflicting with the target branch.
  • CHANGELOG.md
  • docs/reference/testing.md
  • docs/reference/workspace.md
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin ci/darwin-smoke:ci/darwin-smoke
git switch ci/darwin-smoke
Sign in to join this conversation.
No description provided.