Config virtual layers
Configuration is never a single committed file. It resolves through
virtual layers built by the supervisor from the one user-level database
plus three override flags (internal/vconfig).
The three flags
--config-file/-C— a joint config file; may contain aprojects:stanza.--user-config-file— a user-specific config file; may also carry aprojects:stanza.--project-config-file— config for one specific project; ignored in--supervisormode.
Two virtual layers, built in order
- Virtual USER config (low → high precedence):
DB config<--config-file<--user-config-file. - Virtual PROJECTS config (per project):
all projects from the DB< theprojects:stanza from the virtual USER config.
viper does the mechanical merge; internal/vconfig owns the DB layer,
the two-layer USER→PROJECTS composition, projects: stanza extraction,
and the change-vs-override distinction below.
Changes vs. overrides
This distinction only matters for --project-config-file, and only
because the same flag means two different things depending on mode:
- CHANGES happen via
--init(new or re-initialization). A passed--project-config-filehere is merged onto the virtualuser.projectsconfig for that project and persisted to the database. - OVERRIDES happen in normal client mode (no
--init). The same flag here does not touch stored config — it merges on top of the virtual layer at runtime only.
Conflict diffing
If project config arriving via --config-file or a --user-config-file
projects: stanza would override a stored project's settings, the
supervisor diffs stored vs. incoming values
(vconfig.DiffConflicts) and reports every colliding key. The resolution
is either to keep passing the conflicting keys as
--project-config-file (an explicit override, not a change) or to strip
them (vconfig.AutoRemove).
Validation
Validation runs against the fully merged virtual layer
(vconfig.Validate), never against a single source. A missing required
key produces one error naming exactly what's missing, regardless of
whether the gap came from the DB, a file, or a flag.
Provider selection and Ralph-managed pools
A project may select one provider:
provider = "codex"or a round-robin pool:
providers = ["claude", "codex", "opencode"]The plural form is not a persona list. It is an execution policy: each
dispatched plan step receives its own Ralph worker and successive workers are
distributed across the named provider capability records. Ralph therefore
suppresses NativeFanout for pooled bindings; an unordered group remains a
set of visible, independently supervised tasks instead of collapsing into one
opaque provider invocation. The plural key wins when both forms are present.
Empty, non-string, or duplicate pool entries fail loudly.
Provider turn and stall bounds
Provider execution has two independent bounds:
turn_timeoutis the absolute wall-clock ceiling for the completeRunner.Run, including declarative retries. Its default is 30 minutes and its hard maximum is 24 hours.stall_timeoutis the renewable no-progress lease. Any provider stdout or stderr bytes renew it, including partial structured records, but progress never changesturn_timeout. Its default is 3 minutes and hard maximum is 1 hour.
Both values use Go duration strings and must be positive:
turn_timeout = "45m"
stall_timeout = "4m"
[projects."PROJECT_ID"]
turn_timeout = "90m"
stall_timeout = "8m"The virtual USER value is the default for every provider. A project value
overrides it for that project's selected provider or pool. A typed
provider.Request override is task-local and has highest precedence. Omitted
keys retain the bounded defaults; zero, negative, malformed, and over-maximum
values fail before dispatch. The stall value is never reused as the total turn
deadline.
Process-wide concurrency is a supervisor resource limit, not project identity. Set it on the environment of the supervisor process, using the platform-specific form below.
On macOS and Linux, install or update the user service with the environment attached:
# Replace N with an operator-chosen positive-integer emergency ceiling.
radioactive_ralph service install --env RALPH_MAX_PARALLEL=NOn native Windows, SCM install/start is disabled. The supported native process is a control plane running in a foreground PowerShell terminal:
$env:RALPH_MAX_PARALLEL = "N"
radioactive_ralph --supervisorKeep that terminal open and run radioactive_ralph as the client from another
terminal. Native Windows provider PTYs are unsupported, so use WSL2 for
provider-backed execution. Inside WSL2, use the Linux service command above;
the WSL2 systemd --user service is the functional unattended route.
When configured, RALPH_MAX_PARALLEL must be a non-empty integer from 1
through 256. Invalid or explicitly blank values are rejected before the
installed service is rewritten or restarted.
That range is validation, not an operating recommendation. When unset, v0.22
preserves the historical unbounded behavior for compatibility; unbounded is
neither adaptive nor recommended as an optimum.
Project identity
Config is keyed by project ID, not by path. See Architecture for how a project is identified by accumulated fingerprints rather than an absolute path.
