Configuration reference

All settings live in one file: ralph_orchestrator/config.yaml, deep-merged over the defaults in ralph_orchestrator/config.py (config.load). Every key is optional; omitted sections fall back to the defaults shown in the tables below. The shipped file is a fully-commented example with empty secrets.

The environment-override rule

For e-mail and the gateway key, an environment variable wins when set, then the config.yaml value, then (gateway only) the key file:

  • orch.py fills the e-mail environment from the mail: section at startup (_apply_mail_cfg) — only for variables that are still empty, so a live session’s exports always override the file.

  • models.api_key resolves gateway.api_key_env → gateway.api_key → gateway.api_key_file.

Practical consequence: a fresh terminal needs zero exports; a long-lived session can keep overriding the file with export SMTP_PASS=…​ etc. without editing anything.

project:

Key Default Meaning

base_branch

main

the branch ralph/integration is created from; must exist in the target repo (wizard and preflight check it)

integration_branch

ralph/integration

the branch all ticket branches merge into

test_cmd

""

regression command run on the integration branch after every merge; "" skips it (preflight warns); a failing merge is undone with the output as feedback

forbidden_paths

[]

glob list the agents may never change (in addition to the kit and .ralph/), e.g. ["tests/locked/**"]

orchestrator:

Key Default Meaning

agents_ceiling

3

upper bound for concurrent workers; the real limit is computed at runtime from pending-ticket complexity weights (orchestrator.adoc#the-run-loop)

iter_timeout

2700

seconds per agent run (and per acceptance command); a timeout kills the process group (rc 124)

default_max_attempts

4

per-ticket attempt budget unless the ticket sets max_attempts

max_total_agent_runs

200

global agent-run budget; the run halts with BUDGET when reached

push

false

push the integration branch to remote after each merge

remote

origin

remote used when push: true

agent:

Key Default Meaning

cmd

opencode run -m "$RALPH_MODEL" "$(cat "$RALPH_PROMPT_FILE")"

any shell command that edits files in its cwd; the worker sets RALPH_TICKET, RALPH_MODEL, RALPH_PROMPT_FILE, RALPH_WORKTREE (worker.adoc#agent-invocation)

plugins

["@dietrichgebert/ponytail"]

npm plugins merged into the repo’s .opencode/opencode.json so every spawned agent loads them (orchestrator.adoc#plugins)

caveman

true

run the one-time global caveman OpenCode installer when missing (best-effort)

models

S: tef-granite/Granite-4.0-Tiny, M: tef-qwen3-8/qwen3.8-27b, L: tef-gpt-oss-120b/GPT-OSS-120B

ticket complexity → agent model id (as the agent CLI knows it)

gateway:

Key Default Meaning

base_url

https://gateway.tef-ai-lab.edgecloudlabs.com/v1/{path}/openai

OpenAI-compatible base URL; {path} is replaced per model (planner path, reviewer paths)

api_key_env

PORTAL_API_KEY

name of the environment variable that wins when set

api_key

""

direct key value in config.yaml (second priority)

api_key_file

~/.config/opencode/portal.key

key file, last priority

Resolution: api_key_env → api_key → api_key_file (see model access).

llm:

LangChain model specs for the LLM-backed features. Each spec: {path, model, temperature?} (path plugs into gateway.base_url, model is the model name sent to the gateway; temperature default 0.2).

Key Default Meaning

planner

gpt-oss-120b / GPT-OSS-120B / 0.2

the planning model (orch.py plan, and the fallback "engineer" model for contract drafting)

reviewers

3 entries: gpt-oss-120b, qwen3-6 (Qwen3.6-35B), qwen3-8 (qwen3.8-27b)

the consensus panel. Each entry additionally carries name (verdict label) and author_id (the agent model it must not review — matched against agent.models)

consensus:

Key Default Meaning

enabled

true

the review gate (Consensus review panel); false or --no-consensus = acceptance command only

quorum

2

approvals needed, capped by the number of valid votes

max_diff_chars

60000

diff truncation for reviewers (marked [diff truncated])

skip_author_model

true

a model never reviews its own ticket

collaboration:

Key Default Meaning

bus

true

agents get the bus command on their PATH (collaboration.adoc#bus)

contracts

true

parallel waves negotiate interface contracts before launch (collaboration.adoc#contracts)

contract_rounds

3

revision rounds before the mechanical owner decision

max_change_requests

5

cap on approved follow-up fix tickets per run

poll_seconds

5

how often the run loop waits/processes (bus, completions)

report:

Key Default Meaning

every_seconds

600

periodic status mail cadence (10 min)

reply_poll_seconds

30

IMAP reply-poll cadence while a run is active (and the watch cycle cadence)

mail:

Key Default Env override Meaning

to

""

MAIL_TO

receiver(s), comma separated; empty = mail disabled

smtp_host

""

SMTP_HOST

e.g. smtp.gmail.com; empty = mail disabled

smtp_port

587

SMTP_PORT

587 STARTTLS / 465 SSL

smtp_user

""

SMTP_USER

sender login

smtp_from

""

SMTP_FROM

From address (default: smtp_user)

smtp_starttls

1

SMTP_STARTTLS

STARTTLS on port 587; 0 disables it

app_password

""

SMTP_PASS

SMTP app password (never the account password); also the IMAP password default

imap_host

""

IMAP_HOST

reply loop; empty = no reply watching

imap_port

993

IMAP_PORT

993 = always SSL; other ports use STARTTLS per imap_starttls

imap_starttls

1

IMAP_STARTTLS

only used when imap_port is not 993

imap_user

""

IMAP_USER

default: the sender (smtp_from/smtp_user) — replies arrive in the sender’s inbox

imap_pass

""

IMAP_PASS

default: app_password

webhook

""

REPORT_WEBHOOK

optional Teams/Slack/ntfy URL; receives {"text": "<subject>\n\n<body>"}

Secrets: mail.app_password and gateway.api_key are credentials. Fill them for your local runs, keep the committed file at the empty placeholders, and back up filled copies outside the repository. Rotating a credential never requires a code change — the env vars override the file.

The .ralph/ directory

Run state and artifacts live in .ralph/ at the target repo root (git-excluded via info/exclude by the orchestrator and install.sh):

state.json          # single source of truth (xref:data-models.adoc#state-json[])
watch.json          # watch-agent heartbeat
last_report.txt     # last report mail (subject, message-id, body)
inbox.json          # captured operator replies
dynamic.json        # change-request follow-up tickets + added deps
prompts/<id>.md     # exact prompt per attempt
logs/<id>.log       # agent output + guard decisions per attempt
handoff/<id>.md     # what a merged ticket changed
contracts/group<k>.json|.md
bus/messages.jsonl, bus/cursor/<id>, bin/bus
wt/<id>/, wt/_integration/   # git worktrees

Reports go to out/run_report.html and out/run_report.pdf at the repo root.