run() for everyday work, the argv form (run(["cmd", arg])) the moment
any part of the command comes from untrusted input, session() when state must
carry over, and background=True for servers and workers (the idle policy sees
them and keeps the sandbox awake while they live).
Sessions vs. background
They answer different questions. A session keeps state between commands: one shell process stays alive across.run() calls, so cd, export, an
activated venv, and shell functions carry over — exactly like a terminal window
you keep typing into. Filesystem changes persist either way; it is only
process-local state (cwd, environment, shell internals) that dies with each
plain run()’s fresh shell. background=True changes one process’s
lifetime: the call returns immediately and the process outlives it. They
compose — launch a background job from inside a session and keep working.
Sharp edges
A synchronous call has ~110 s at the edge — longer work belongs inbackground=True or a session, collected later. One argv string caps at
128 KiB (kernel limit). stdout/stderr are UTF-8 text capped at 16 MiB per
stream (ExecResult.truncated + an inline notice mark a cut) — big or binary
output goes to a file. Background execs return no pid — record one yourself
(cmd & echo $! > pid) and redirect output to a file or it is discarded.