Skip to main content
Setting the GC_AGENT_SLICE environment variable to a systemd user slice (for example gascity-agents.slice) makes the tmux session provider wrap every pane’s initial command in a transient systemd user scope:
systemd-run --user --scope --slice=<slice> --collect --quiet -- sh -c '<command>'
Default-off: when the variable is unset or empty, pane commands run unwrapped exactly as before.

Why

systemd-enabled tmux builds (stock Ubuntu) move every pane into a transient tmux-spawn-*.scope under the default user slice, so agent processes escape whatever slice the tmux server itself runs in. Wrapping the pane command re-parents the agent’s process tree into a dedicated user slice where resource weights (CPUWeight, MemoryHigh, …) can be applied to all agents collectively.

Scope and activation

  • Tmux provider only. The subprocess and exec session providers spawn children of the gc process directly, so those already inherit gc’s own cgroup; only tmux panes escape and need re-parenting.
  • Env var, not city.toml. This is a host-level deployment knob, not per-city configuration: it is set by whatever supervises the gc process (a systemd unit, shell profile, or CI environment) and applies to every city served by that process. The slice it names is host systemd state that must exist on the user manager, outside any city’s config layering.
  • Keep the value stable for the process lifetime. The availability probe runs at most once per tmux provider instance, for the first non-empty value that instance sees; a value changed while gc is running is embedded in later wrapper commands without being re-probed. gc constructs provider instances both long-lived (the controller’s reconcile loop) and fresh per operation (template session starts), so a changed or repaired slice takes effect on some spawn paths and not others. Restart gc to converge every path on one verdict.

Probe and fallback

Before its first wrapped spawn, each tmux provider instance probes systemd-run --user --scope --slice=<slice> --collect --quiet -- true (bounded at 5 seconds). If the probe fails — no systemd-run binary, no reachable user manager, or an invalid slice — that instance logs one warning and every pane command it spawns runs unwrapped:
tmux agent slice: GC_AGENT_SLICE="..." set but transient user scopes are unavailable; pane commands run unwrapped: ...
Because operations like template session starts construct fresh provider instances, a persistently broken host repeats this warning as new instances probe, while long-lived instances (the controller’s reconcile loop) keep their first verdict until restart. The probe runs in the gc process’s environment, while pane commands execute with the tmux server’s environment. gc normally spawns the tmux server itself, so the two match; if you point gc at a pre-existing tmux server whose global environment lacks a reachable user bus (XDG_RUNTIME_DIR, DBUS_SESSION_BUS_ADDRESS), wrapped spawns can fail even after a successful probe. The systemd-run error is visible in the dead pane’s captured output in startup diagnostics.

User-manager lifecycle coupling

Wrapped agents live under the user’s user@<uid>.service manager. Ending that user session — loginctl terminate-user, or logging out without lingering — kills every agent scope. For unattended hosts, enable lingering so the user manager (and the agents) survive logout:
loginctl enable-linger <user>

Resource attribution

Scopes are created with systemd auto-generated names (run-rNNNNNNNN.scope), so systemd-cgls --user shows anonymous units under the slice rather than per-agent names. To attribute a scope to an agent session, list the processes inside it:
systemd-cgls --user --unit <slice>
ps -o pid,args --forest -g <pid-from-cgls>
The agent command line (and its tmux session name, via tmux list-panes -a -F '#{session_name} #{pane_pid}') identifies the owner.

Detection

A wrapped pane reports pane_current_command = "systemd-run" instead of the agent process name. All gc liveness, zombie-cleanup, and pane-finding paths handle this by walking pane process descendants, so health patrol and nudge targeting behave the same with wrapping on or off.
Last modified on June 13, 2026