fix: headless entrypoint needs sway/grim, and must reap sway by real pid
Two blockers once XDG_RUNTIME_DIR (ci-tron's own fix) let the headless
smoke test actually start:
- flake.nix's devShell already bundled wlrctl/wl-clipboard for this
exact test but never sway or grim themselves — scripts/headless_test.py
needs both, and had no way to get them on a host that doesn't happen
to have a desktop environment already providing them system-wide (a
bare CI box, for instance).
- On a host with no logind session for this seat — nothing registers
one for ci-tron-runner's sudo session, same root cause as the
XDG_RUNTIME_DIR fix — sway double-forks and detaches, reparenting to
init. start_sway's own Popen then tracks a pid that's already exited,
not the real compositor: find_sway_ipc_socket was matching the IPC
socket by that stale pid (so it could never find sway's real one) and
the process a broken run's `finally` tried to terminate() wasn't the
real compositor either, leaking sway/swaybar/swaybg past every run —
confirmed by hand (orphaned sway processes still running from a
reproduction). Recovers the real pid from the IPC socket's own name
instead ("sway-ipc.<uid>.<pid>.sock" — authoritative, unlike the
Popen handle), and reaps it with a plain kill(2)/proc-poll loop since
it's not our child (waitpid(2) can't see it).
Verified end-to-end as ci-tron-runner itself on the ci-tron host,
including confirming no leaked processes across repeated runs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KpDBFeBQKx9f5U3pfVeVV3