ADR 0055: The launcher owns every local service
Status: accepted (James, 2026-07-25). Applies to the single-service pi architecture.
Context
Clankie is present through several long-lived local processes. Starting them by hand leaves no durable ownership record, no dependency-aware restart, and no reliable health gate.

Decision
apps/tui/bin/service-supervisor.ts owns the process mechanics and
apps/tui/bin/services.ts declares the services and their dependency order.
The backend is one clankie process: the HTTP API, pi captain, presence state,
media, and game bodies all live there. The Discord bridge depends on it. The
optional lab user-session body depends on it too and stays off until enabled
(ADR 0098 (user-session shares)). The activity
surface and tunnel publish what he plays.
Every managed process gets:
- an atomically written mode-0600 pid record under the Clankie state root;
- a live command check before any signal, so a recycled pid cannot kill an unrelated process; and
- a service-specific health gate before start succeeds.
Unowned conflicts are scoped to the resource the service uses: the configured
TCP port for Clankie, relay and activity, or the configured activity tunnel
name. A process command identifies the kind of service but cannot distinguish
instances whose ports and state roots travel in environment variables.
lsof supplies address-independent listener evidence; a bind probe cannot
reliably detect wildcard versus loopback conflicts on macOS. Failed inspection
falls back to the conservative command scan. Discord bodies keep the command
scan because a separate local port does not establish a separate Discord identity.
Health probes and the live-command check before signalling retain their own roles.
The force-kill deadline is also service-specific. Generic services retain the
launcher's ten-second grace. Clankie's outer grace is its configured play
shutdown deadline plus two seconds, leaving its play host time to finish a
normal summary or publish the bounded forced terminal report before the
supervisor escalates to SIGKILL.
Restart follows dependencies. Restarting clankie also restarts the bridge
and the lab user-session body, because both hold live claims against the
service instance. Stopping one named service remains scoped to that service.
A restart requested from Clankie's own operator-turn bash tool is handed to a
detached launcher helper. Pi already exposes the durable PI_SESSION_FILE; the
launcher uses its conversation's append-only event log to wait for that turn's
terminal event before stopping the service. The operator face retries only a
dropped durable tail read, then resumes from its persisted cursor. It never
replays the prompt or any tools that already ran.

Editable Turbopuffer tldraw source
The compatibility aliases captain, captain-eve, eve, control-plane, and
cp all resolve to clankie; they do not name separate processes.
Consequences
clankie restartandclankie statuscover the full local stack. The headless command contract isdocs/cli.md.- A self-restart finishes the conversation turn before replacing its backend; a dropped tail reconnects without repeating the turn.
- A process started outside the launcher is reported but never adopted or killed.
- Restarting during play preserves terminal accounting instead of leaving the next process to infer an avoidable lease lapse.
- Settings remain the source of Discord allowlists; the launcher supplies only repository paths and brokered service credentials.
