Task: Add compass site start/stop/status (detached lifecycle)

Table of Contents

This page documents a task in the Documentation improvements story. It captures the goal, current status, acceptance, and any notes or results.

Goal

`compass site serve` currently only runs as an ad hoc foreground/background shell process (manually backgrounded, killed via `pkill`). Give it a proper systemd-managed lifecycle – `compass site start`/`compass site stop`/`compass site status` – backed by a real `systemctl –user` unit, matching every other ORE Studio process's lifecycle instead of inventing another bookkeeping mechanism.

(This went through two implementations. The first pass used a PID-file lifecycle matching the Qt client's, reasoning by analogy from `compass_services.py`'s own docstring – the client was never DB-registered either, and opted out of systemd for that reason. On review, that analogy doesn't actually hold: systemd doesn't need a DB-registered service_definition to manage a process at all – that requirement is specific to the fleet's dependency-graph generator, not to systemd itself. A concrete, standalone unit works fine for a single local dev tool with no DB dependency, and is strictly better than PID files (cgroup-owned process tracking, no PID file to go stale, journald logging for free). Redone as a real unit; see `* Plan`.)

Status

Field Value
State DONE
Parent story Documentation improvements
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-08-04

Acceptance

  • `compass site start`/`compass site stop`/`compass site status` start/stop/report the site-preview server via a real `systemctl –user` unit, replacing manual backgrounding (nohup/pkill) and the earlier PID-file lifecycle.
  • No orphaned processes left behind after stop – systemd owns the process via its cgroup, not a PID file that can go stale.
  • `start` does not rebuild the site (that stays a separate `compass build –direct site` step); `compass site serve` (the existing foreground command, `–compile` included) is unchanged for interactive terminal use.

Plan

Added `render_site_unit()` to `systemd_generate.py`, alongside the existing `render_nats_unit()` – both are concrete, non-DB-driven, standalone unit renderers (the module's own docstring already explains why every unit here is concrete rather than templated: an earlier `%i`-specifier version had a real systemd bug that wedged the user manager on daemon-reload). `Type=simple`, `ExecStart=/usr/bin/ python3 -m http.server <port> –directory <build_dir>` directly – no shell wrapper or `EnvironmentFile=` needed, since the port and directory are both known at generation time. Deliberately not `PartOf=`/`Wants=`-linked to `ores-<env>.target`: previewing the site is an occasional dev convenience, not something every fleet start/stop should cascade to.

`compass.py` gained `_site_generate_and_deploy()` (render + write to `systemd/units/` + copy to `~/.config/systemd/user/` + `daemon-reload` only if changed – the same add/update-only-if-different shape as `systemd_generate.cmd_deploy`, scoped to just this one unit rather than re-running the full DB-driven pass) and a `_cmd_site_start/stop/status` trio that shells out to `systemctl –user start/stop/is-active` on the generated unit name (`site-<env>.service`). `start` checks the build directory exists first (erroring with the `compass build –direct site` hint if not) but never triggers a rebuild itself.

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
#1829 [compass] Add compass site start/stop/status detached lifecycle

Review

Comment summary File Decision Notes
       

Result

Real `systemctl –user`-managed unit (`site-<env>.service`), not PID files. Hand-verified: `start` renders+deploys the unit, starts it, and prints the URLs; `systemctl –user status` shows a genuine cgroup with the correct `python3 -m http.server` command line and a real PID; `curl` against the served port returns 200; `stop` cleanly stops the unit with no orphaned process; `status` correctly reports active/inactive. Deleted the earlier PID-file implementation's stray log file (`build/output/site-run/`) and updated How do I serve the site locally? and its cross-link from How do I start the ORE Studio services? to describe the new approach.

Emacs 29.3 (Org mode 9.6.15)