Task: Migrate Qt client to a proper systemd unit

Table of Contents

This page documents a task in the Systemd resource management and per-environment isolation story. It captures the goal, current status, acceptance, and any notes or results.

Goal

Every other ORE Studio process is now started/stopped/tracked via systemctl --user (concrete, non-templated units generated from systemd_generate.py). The Qt client is the one exception: it still uses its own _launch=/=_terminate PID-file bookkeeping (compass_services.py). Give it a real systemd unit – one concrete unit per colour instance (red/green/blue/uncoloured can all run simultaneously today under the PID-file scheme; the systemd version must preserve that), following the same "site-serve" precedent (compass site start/stop/status) once that lands.

Status

Field Value
State ABANDONED
Parent story Systemd resource management and per-environment isolation
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-08-04

Acceptance

  • `compass client start/stop` drives a real `systemctl –user` unit per colour instance (concrete, not templated – same lesson the fleet's own units already learned about `%i` specifiers), not the current PID-file `_launch`/`_terminate` path.
  • Multiple colour instances (red/green/blue/uncoloured) can still run simultaneously, matching current behaviour.
  • `compass client stop –colour <x>` stops only that instance's unit.
  • No orphaned `ores.qt` processes left behind after stop.

Plan

Before implementing, verified empirically whether a GUI Qt process can even run under systemctl --user on this environment (WSLg/XWayland remote X). It cannot – see * Result. Abandoned once that was confirmed reproducible; no unit-generation code was written.

Notes

Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario, run through the QA Validation Runner panel) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

PRs

PR Title
#1833 [agile] Abandon Qt client systemd migration: X display unreachable

Review

# Comment summary File Decision Notes
1 story.org State=STARTED but Now/Next/Last touched stale (4 review passes flagged this) story.org Accepted No task in this story is actually in flight (the one started task was abandoned) – reverted State to BACKLOG rather than inventing a fake Now/Next.
2 Comma splice / missing connector in Result prose task_migrate_client_to_systemd.org Accepted Reworded "…or a broken binary, specifically…" to "…or a broken binary; specifically, …".

Result

Abandoned after empirical testing showed a real, reproducible blocker, not just an architectural-consistency nice-to-have:

  • Launched ores.qt via a transient systemd-run --user unit with DISPLAY explicitly set (--setenv=DISPLAY=neumann:11.0): failed immediately with qt.qpa.xcb: could not connect to display neumann:11.0, process aborted (SIGABRT).
  • Retried with XAUTHORITY also explicitly set to the working ~/.Xauthority cookie file (confirmed via xauth list to contain a valid MIT-MAGIC-COOKIE-1 entry for that exact display): same failure, same crash.
  • The identical binary launches and renders correctly via a plain subprocess (i.e. exactly what compass client start's current PID-file _launch does, and what this whole session's screenshot work relied on) – so this isn't a broken X server or a broken binary; specifically, the systemd --user session boundary on this WSLg/XWayland setup isn't inheriting (or isn't authorized for) the interactive login session's X access the same way a normal child process is.

Every other ORE Studio process is headless, so the fleet's migration to systemd never had to cross this boundary. Root-causing why systemd user sessions can't reach the X display here (session scoping? cgroup/namespace isolation specific to WSLg? something else entirely?) is a real, separate investigation with no guaranteed payoff – and even if solved, the client's per-invocation dynamic args (colour, --open-scenario path, --master-password) are a structurally worse fit for systemd's generate-once-at-config-time unit model than the fleet's static, DB-driven service definitions ever were. The PID-file lifecycle it has today already satisfies the actual goal (no orphans, clean start/stop) – it was inconsistent with the rest of the fleet, but not actually broken. Not worth pursuing further without a concrete reason beyond consistency.

Emacs 29.3 (Org mode 9.6.15)