Architecture

How it's built.

Ralpharium is a small Python daemon that sits next to your repo. It reads the markdown files your AI runner cares about, spawns the runner as a subprocess, watches what comes out, and streams everything to a dashboard in your browser. Nothing leaves your machine. No accounts, no databases — just files, processes, and a WebSocket.

Home

Local-first. The whole product runs on one Python process on your machine. There is no server you pay for, no SaaS to log into. The dashboard is just a static frontend served by the same daemon at localhost:3000.

The runner is the AI. Ralpharium doesn't replace Claude Code, Codex, or Aider — it runs them. You pick which one in the dashboard. Each iteration spawns the runner as a subprocess, the daemon captures stdout, parses what came out, and records the result. Swap runners by changing one config field.

The four input files the runner reads each iteration: PROMPT.md (instructions), AGENTS.md (rules), IMPLEMENTATION_PLAN.md (task list), and specs/*.md (acceptance criteria). The daemon parses all four and surfaces them on the dashboard.

┌─────────────┐    reads     ┌─────────────────────────┐    streams    ┌──────────────┐
│ your runner │ ──────────►  │   ralph studio daemon   │ ─────────────►│  dashboard   │
│ (codex /    │   PROMPT.md  │  • repo inspector       │   websocket   │ (operator)   │
│  claude /   │   PLAN.md    │  • plan parser          │               │              │
│  aider)     │   AGENTS.md  │  • spec coverage        │               │              │
│             │   specs/*    │  • backpressure runner  │               │              │
│             │   git diff   │  • iteration store      │               │              │
│             │              │  • RAM event stream     │               │              │
└─────────────┘              └─────────────────────────┘               └──────────────┘
                                       │
                                  .ralph/iterations.jsonl  (append-only history)

Three execution surfaces

1

Disk

durable

The Ralph artifacts (PROMPT.md, AGENTS.md, IMPLEMENTATION_PLAN.md, specs/*) plus .ralph/iterations.jsonl. Survives restarts.

filesJSONL
2

RAM

volatile

Live event stream, blackboard slots, scratchpad, in-memory checkpoints. Wiped on daemon restart.

ring bufferWebSocket
3

Process

runner

The configured runner (codex, claude, aider, custom shell) — spawned as a subprocess; stdout streamed live.

asyncioshell

Ralph artifacts

Every iteration starts by re-reading the same files. Studio parses each one and surfaces structure to the dashboard.

file
role
what Studio extracts
PROMPT.md
per-iteration instruction
existence, modified time, preview, mode
AGENTS.md
operational rules
existence, preview, suggested rules from history
IMPLEMENTATION_PLAN.md
persistent state
tasks (done / next / blocked / stale), drift warnings
specs/*.md
source of truth
per-spec coverage status from plan + commits

Plan parser

The parser reads checkbox tasks (- [ ], - [x], plus [/] in-progress, [!] blocked, [~] stale) and groups them by section. It picks "next" as the first pending task and raises warnings for repeated text, all-completed plans, and 24h+ staleness.

Iteration model

Each loop pass is one Iteration. Studio creates one when you click Start (or when your runner POSTs to /api/iterations) and finalizes it on success / failure / stop. The full record is appended to .ralph/iterations.jsonl — a flat file you can tail -f, grep, or ship anywhere.

{
  "id": "it_1736172000_a3f9c1d8",
  "number": 42,
  "mode": "build",
  "status": "passed",
  "started_at": 1736172000.12,
  "ended_at": 1736172094.55,
  "summary": "Add /api/specs endpoint",
  "files_changed": ["backend/main.py", "backend/specs.py"],
  "commit_sha": "9af2b7c",
  "test_status": "passed",
  "validation": [{"name": "Tests", "status": "passed"}],
  "command_output": "...",
  "failure_reason": null
}

The dashboard timeline lists the latest 30; click any row and the replay drawer fills with the prompt mode used, the validation results, the files changed, the tail of stdout, and the commit it produced.

Backpressure (validation)

Backpressure is just a name for "the loop stops when tests fail." Ralpharium looks for npm test / npm run lint / npm run build in your package.json and offers them as one-click checks. If you turn on stop_on_failure, a red check halts the loop before bad code lands. Same idea for git: turn on stop_if_dirty_before_run and Ralph won't start a new iteration on uncommitted changes.

Auto-detect

Reads package.json scripts (test, lint, typecheck, build). Falls back to pytest -q for Python projects.

Status & output

Each check tracks status, last-run timestamp, duration, and the tail of stdout — exposed at /api/backpressure and live over WS.

Guardrail learning

Repeated failures turn into AGENTS.md suggestions: "Tests failed 3× — require npm test before commit."

RAM observability

Beyond the durable iteration log, Studio keeps a volatile RAM layer that the dashboard can stream live. Nothing here is written to disk — it's the live nervous system of the running daemon.

Event stream

Ring buffer of typed events: boot, iteration start/end, validation, scratchpad notes, checkpoints. Capped at 600 entries.

deque600 entries

Blackboard

Named volatile slots: loop_mode, runner, current_task, last_error, last_commit, files_changed.

kv slotsvolatile

Scratchpad

Pin a thought during the session. Survives across iterations, dies on restart. Useful for "the spec was wrong" notes you'll lose otherwise.

notessession-only

Checkpoints

Snapshot the repo + plan + prompt at any moment. Compare two checkpoints to see what changed during the session.

repo+planin-memory

Memory pressure

Real numbers: prompt context bytes, repo scan size, event buffer size, runner RSS — surfaced as bars in the dashboard.

fs + psreal

Process

The runner subprocess: PID, RSS via ps/tasklist, command, runtime. Live stdout streams over WebSocket.

asynciostdout

API surface

Everything is REST + a single WebSocket. The dashboard only uses these — your CLI can use them too.

GET   /api/state                 # aggregate first-paint snapshot
GET   /api/status                # mode, iter count, runner, branch, dirty
GET   /api/repo-state            # git + Ralph file detection
GET   /api/plan-health           # IMPLEMENTATION_PLAN.md parsed
GET   /api/spec-coverage         # specs/* mapped to plan + commits
GET   /api/backpressure          # auto-detected validation checks
GET   /api/guardrails            # PROMPT/AGENTS + suggestions
GET   /api/iterations?limit=N    # iteration history
GET   /api/iterations/{id}       # single iteration detail
POST  /api/iterations            # CLI hook: begin iteration
PATCH /api/iterations/{id}       # CLI hook: finish/update iteration

POST  /api/loop/{start|pause|resume|stop|panic}   # control
POST  /api/check/{id}            # run a backpressure check
POST  /api/runner                # set runner + command

GET   /api/ram                   # full volatile snapshot
GET   /api/ram/events            # ring buffer
GET   /api/ram/blackboard        # kv slots
GET   /api/ram/pressure          # memory pressure
POST  /api/ram/scratchpad        # pin a note
POST  /api/ram/checkpoints       # snapshot repo+plan

WS    /ws                        # live updates: status, iteration_*, ram_*, log

Stack & run

PY

Backend

  • Python 3.11+
  • FastAPI · Uvicorn
  • asyncio.subprocess
  • stdlib only — no NumPy, no DB
  • JSONL persistence
JS

Frontend

  • Vanilla HTML · CSS · JS
  • WebSocket client
  • Inline SVG, no canvas
  • Responsive 12-col grid
  • Zero build step
SH

Run locally

pip install -r backend/requirements.txt
python backend/main.py
# http://localhost:3000

# optional config
RALPH_REPO_PATH=/path/to/repo \
RALPH_RUNNER=claude \
RALPH_RUNNER_CMD="claude -p PROMPT.md" \
python backend/main.py
Console

See it run.

Open the live console and watch every Ralph iteration — plan, validation, commit — through one observable surface.