Architecture#

One binary, two modes#

  • radioactive_ralph --supervisor — the long-lived supervisor. It owns every agent subprocess’s pty (creack/pty, internal/agent), holds all work open, serves the discovery socket (internal/ipc), runs the reaper, and owns the one user-level SQLite database (internal/store). Working directory is irrelevant to it.

  • radioactive_ralph (no flag) — a dumb client. It discovers the supervisor, resolves the current directory to a known project (auto-initializing if needed), and renders a read-only Bubble Tea TUI. It refuses to run without a live supervisor.

  • radioactive_ralph --init — explicitly registers or re-registers the current directory as a project.

  • radioactive_ralph service {install,uninstall,status} — manages the supervisor as a per-user OS service (launchd/systemd/Windows SCM) so it survives logout/reboot/crash.

  • radioactive_ralph doctor — environment checks (git, provider CLIs, service manager).

The plan command group (plan import <file>, plan ls) seeds and lists plans for the current project — resolved by fingerprint, not by a committed per-repo config. Beyond that there are no other command-level subsystems: no variant/persona selection, no attach/cockpit framing. The client is the read-only view; there’s nothing separate to attach to.

The control invariant#

An agent CLI must never block the system. The supervisor owns each agent’s pty directly and runs a watchdog that classifies stream output as progress, stall (no-output-for-N), or an interactive-prompt pattern — any of which triggers auto-resolve, deny, or kill-and-reclaim. See Safety floors.

Discovery#

The supervisor binds a Unix domain socket (a named pipe on Windows) at a well-known path under the XDG runtime state root (ipc.ServiceEndpoint: <state-root>/service.sock, heartbeat service.sock.alive). Discovery is just dialing that socket:

  • success → a supervisor is live

  • failure → refuse, and print the command to start one

Single-instance is enforced by an exclusive flock on a PID lockfile, not by the socket bind — the PID lock plus heartbeat file distinguish a live supervisor from a stale socket left by a crashed one (dead PID → the next supervisor reclaims: remove the stale socket, take over).

State: one user-level database, clean repos#

All project, plan, config, and spend state lives in one user-level SQLite database under the XDG state root — durable memory for every registered project on the machine, not per-repo. There is no committed config directory and no per-repo database. Never store Ralph runtime state under .claude/.

Project identity#

A project is identified by accumulated fingerprints, not an absolute path:

  • a git directory fingerprints via git heuristics (root-commit sha, remote, repo-root markers)

  • a non-git directory seeds with its absolute path

Identifiers accumulate: a directory that starts as path-only and is later git init-ed gains its git fingerprints on top of the existing path identifier, so the same project stays recognized across that transition and across directory moves.

Config#

Configuration resolves through virtual layers built by the supervisor from the database plus three override flags (--config-file/-C, --user-config-file, --project-config-file). See Config virtual layers for the full model.

Plans and completion#

Plans are markdown, decomposed heuristically over a goldmark AST — no LLM in decomposition. The orchestrator dispatches ready steps to agent workers with plan-scoped context and verifies completion against acceptance criteria; a worker’s self-report or process termination is never sufficient on its own. See Plan format and Orchestrator-verified completion and A2A.

Providers#

Shipped providers: claude, codex, opencode — each a capability record, not a persona. “Local-only” means the CLI owns its own agent loop and tool execution locally, even when it calls a hosted model for inference. gemini was removed (CLI auth endpoint deprecated 2026-06-18); cursor-agent is excluded (delegates session control to Cursor’s cloud). See Provider contract.