Story: Systemd resource management and per-environment isolation
Table of Contents
This page documents a story in Sprint 25. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
On 2026-07-31 the machine was rebooted: every process was killed under
memory pressure. compass services starts processes with a plain
subprocess.Popen(start_new_session=True) – no cgroup, so each
service is accounted to whatever launched compass (Emacs, a Claude
session, a bare shell). Claude sessions do land in app-claude.slice,
but flat (no per-environment split) and with no MemoryMax anywhere on
the box, so kernel OOM is a global decision that can pick off Emacs
or another environment's fleet.
This story makes compass the single wrapper for process lifecycle
across the fleet: compass services start, compass claude, compass
services status all place work in the right cgroup, with the right
limits, and report per-environment – so a runaway environment cannot
take down Emacs, the machine, or another environment's fleet.
Full analysis, findings, and the workstream design live in the resource-management design note; this story tracks the implementation.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 25 |
| Now | Nothing. All unprivileged workstreams (WS-7, WS-1, WS-5 unpriv, WS-4) merged; WS-3 and WS-2 both discovered already implemented (predate this story, Sprint 24 PR #1806); the client-migration and controller-decommission investigation tasks closed (abandoned/done). |
| Waiting on | Someone with root, for WS-5's root half – left as a follow-up, not blocking this story's close. |
| Next | Nothing under this story. WS-5 (root half) and the deliberate-OOM acceptance test, if picked up, belong to a new story; WS-6 stays deferred. |
| Last touched | 2026-08-05 |
Acceptance
compass claudelaunches intoapp-claude-<env>.slice, andsystemctl --user showon that slice reports a non-infiniteMemoryMax.compass services startresults in service processes whose/proc/<pid>/cgroupis under the environment's slice, regardless of whether it was invoked from Emacs, a Claude session, or a bare shell.compass services statusshows exactly the current environment's fleet, including when nothing is running.- Deliberately exhausting memory in one environment kills only that
environment; Emacs and other fleets survive. Tested explicitly, with
MemoryMaxtemporarily lowered so the test is quick and safe. compass build --statusreports two slots, and a running build appears underapp-build-<env>.sliceinsystemd-cgls.- No
compasssubcommand requires root for normal operation.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Per-environment systemd resource limits (Claude slices, services, builds) | DONE | 2026-08-05 | 2026-08-05 | Implement WS-1 through WS-5 and WS-7 of the resource-management plan: per-environment Claude slices with MemoryHigh/MemoryMax/MemorySwapMax/CPUWeight limits (WS-1); converge compass services onto systemctl –user against the generated ores@ENV.target units, staged behind –legacy (WS-2); compass deploys the generated units instead of printing instructions (WS-3); per-environment status/tree/top views (WS-4); protect Emacs and the interactive session via slice drop-ins (WS-5, unprivileged half); reduce build lock to two slots and run builds inside a memory-capped app-build-ENV.slice (WS-7). See systemd-resource-management-plan.org for full analysis, design decisions, and sequencing. WS-6 (PID 1 ownership) is explicitly deferred and out of scope for this task. |
| Migrate Qt client to a proper systemd unit | ABANDONED | 2026-08-04 | 2026-08-04 | Abandoned: systemd –user sessions on this WSLg/XWayland setup cannot reach the X display (confirmed empirically), a real blocker beyond mere architectural inconsistency. See the task's Result. |
| Investigate decommissioning ores.controller.service and its DB tables | DONE | 2026-08-04 | 2026-08-04 | Binary/code confirmed already fully gone (Sprint 24, PR #1806). service_definitions/dependencies_tbl confirmed still load-bearing (systemd_generate.py's source of truth). service_instances_tbl/service_events_tbl confirmed genuinely dead (zero readers/writers repo-wide) – filed as a follow-up capture rather than dropped speculatively here. See the task's Result. |
Decisions
Out of scope
- Separate user accounts per environment.
- True namespaced
systemctl(one manager per UID); a per-environment view through compass is accepted as sufficient until thesplit-services-into-per-service-containerswork gives each environment its own PID namespace. - Hard CPU quotas (
CPUQuota) –CPUWeightis used instead so-j3build bursts on an idle machine are never penalised. - PID 1 ownership (WS-6) – deferred; only pursued if the unit count keeps causing problems after the other workstreams land.