uv's cache moves into the workspace so it shares a filesystem with
.venv and hardlinks instead of double-storing ~1.3 GB per job (the
'Failed to hardlink, falling back to full copy' warning was the tell
before ENOSPC), and both matrices run max-parallel 1 so the host
holds one dependency set at a time instead of six.
github.ref reaches the shell via env instead of ${{ }} interpolation
- a crafted PR branch name could otherwise inject commands into the
runner - and ref/sha formats are validated before git sees them.
act_runner injects no node runtime into job containers the way
GitHub's runners do, so the JS checkout action dies in plain distro
images with 'node: executable file not found in $PATH'. A git
init/fetch/checkout needs only git, which each distro setup step now
installs before checkout runs.
Gitea Actions matrix: ubuntu-latest (py3.11/3.13) plus Debian 12,
Ubuntu 24.04, Fedora and Arch containers, each installing the system
libraries PySide6 wheels dlopen - all four validated locally in
Docker (Debian 141/141, QtWidgets import probe green everywhere).
Windows/macOS and a NixOS-FHS job (tools/ci-fhs.nix, the library set
from docs/NIXOS.md) are gated to github.com runners so they skip
instead of queueing forever here. README gets a Platform support
table with the workflow badge; KiCad-in-the-loop stays field-tested,
CI covers the solver and fallback suite.