How the Ralph Persistent Evolutionary Loop Survives Session Boundaries in Ouroboros

The Ralph persistent evolutionary loop achieves cross-session durability through event sourcing and a stateless architecture that reconstructs lineage state from an append-only EventStore rather than maintaining long-running process memory.

The Ouroboros repository implements an autonomous ontology evolution system where Ralph serves as the "always-on" driver that repeatedly invokes the evolutionary step function until convergence. Unlike traditional long-running daemons, Ralph’s persistent evolutionary loop is engineered to survive crashes, restarts, and manual interruptions without data loss.

Stateless Architecture: Fresh Processes per Cycle

Ralph operates on a stateless cycle design where each iteration executes in a fresh Python process. The primary entry point, scripts/ralph.py, contains the connect_and_run function which establishes a stdio connection to the MCP server and the _call_evolve helper that dispatches ouroboros_evolve_step calls.

Because the script never maintains long-lived state between iterations, it only receives the JSON result of the latest evolution step and decides whether to:

  • Continue to the next generation
  • Retry via ouroboros_lateral_think when stagnation is detected
  • Terminate when convergence is reached

This statelessness ensures that killing the process at any point never corrupts the evolutionary lineage.

Event Sourcing with EventStore

All actions performed during evolution are persisted as immutable events in the EventStore located at src/ouroboros/persistence/event_store.py. The store utilizes an append-only log architecture, meaning every seed creation, tool execution, QA verdict, and evolution step becomes a permanent, timestamped record.

The EventStore enables complete lineage reconstruction through event replay. When Ralph restarts, it does not restore from a snapshot; instead, the system reads the entire event history to rebuild the current state deterministically.

Lineage-Based Session Continuity

Each evolutionary run is identified by a unique lineage ID that bridges session boundaries. The persistence mechanism works as follows:

  1. The first generation creates a lineage.created event
  2. Subsequent generations append evolve_step.completed events
  3. The MCP tool handler in src/ouroboros/mcp/tools/definitions.py processes these events to determine the next state

When Ralph is restarted with an existing --lineage-id, the next ouroboros_evolve_step call receives the same identifier, the MCP server reads the existing events from the store, and the loop continues from the last successful generation. This design allows Ralph to "pick up where it left off" transparently after system crashes or manual restarts.

Convergence Detection and Loop Control

The evolutionary loop terminates based on semantic similarity metrics returned by each step. The ouroboros_evolve_step tool returns a markdown block containing an **Action** field with three possible values:

  • continue – Proceed to next generation
  • stagnated – Trigger lateral thinking or retry logic
  • converged – Exit the loop with code 0

Convergence is achieved when the similarity score reaches ≥ 0.95, indicating the ontology has stabilized across generations.

Git Tagging and Rewind Capabilities

While the EventStore serves as the authoritative source of truth, scripts/ralph.sh provides optional git tagging for human-readable checkpoints. After every successful generation, the tag_generation function creates a tag formatted as ooo/{lineage_id}/gen_{N}.

For recovery scenarios, scripts/ralph-rewind.py implements the ouroboros_evolve_rewind tool. This utility reads the EventStore to locate a target generation and can optionally checkout the corresponding git tag, enabling precise rollback to any point in the evolutionary history.

Practical Usage Example

Initialize a persistent Ralph loop with a seed file and maximum cycle limit:

ooo ralph \
    --lineage-id my_lineage \
    --seed-file examples/dummy_seed.yaml \
    --max-cycles 50

During execution, ralph.py prints JSON status objects to stdout. A typical mid-run response appears as:

{
  "action": "continue",
  "generation": 3,
  "lineage_id": "my_lineage",
  "similarity": 0.87,
  "next_generation": 4,
  "lateral_think_applied": false,
  "qa": {"verdict":"pass","score":92.3,"error":null},
  "error": null
}

Upon reaching the convergence threshold, the output changes to:

{
  "action": "converged",
  "generation": 7,
  "lineage_id": "my_lineage",
  "similarity": 0.96,
  "next_generation": null,
  "lateral_think_applied": false,
  "qa": {"verdict":"pass","score":95.0,"error":null},
  "error": null
}

If the process terminates after generation 4 and is later restarted with the same --lineage-id, Ralph queries the EventStore, reconstructs the state through generation 4, and automatically issues the next evolve_step call with generation=5.

Summary

  • Stateless design: Each Ralph cycle runs in a fresh process via scripts/ralph.py, eliminating memory-related failure modes.
  • Event sourcing: The EventStore in src/ouroboros/persistence/event_store.py maintains an append-only log of all evolutionary events.
  • Lineage continuity: Unique lineage IDs bind generations across sessions, with state reconstruction handled by the MCP tool in src/ouroboros/mcp/tools/definitions.py.
  • Convergence criteria: The loop exits when similarity scores reach 0.95 or higher, signaled through the action field in step results.
  • Recovery tools: scripts/ralph-rewind.py and scripts/ralph.sh provide git-based checkpointing and temporal rollback capabilities.

Frequently Asked Questions

How does Ralph resume after a system crash?

Ralph resumes by reconstructing state from the EventStore rather than restoring from memory. When restarted with the same --lineage-id, the ouroboros_evolve_step handler in src/ouroboros/mcp/tools/definitions.py replays all lineage.created and evolve_step.completed events to determine the current generation, then continues execution from that point.

What convergence threshold stops the evolutionary loop?

The loop terminates when the similarity field in the step response reaches 0.95 or higher, triggering an action value of converged. This causes scripts/ralph.py to exit with status code 0.

Can I rewind to a specific previous generation?

Yes. Execute scripts/ralph-rewind.py with the target lineage and generation number. This script invokes the ouroboros_evolve_rewind MCP tool, which queries the EventStore to validate the generation exists and can optionally checkout the corresponding ooo/{lineage_id}/gen_{N} git tag created by scripts/ralph.sh.

Why does Ouroboros use event sourcing instead of a traditional database?

Event sourcing in src/ouroboros/persistence/event_store.py provides immutable audit trails and deterministic replay capabilities required for reproducible ontology evolution. Unlike mutable database records, the append-only event log guarantees that any session can be reconstructed identically across different machines or after catastrophic failures, which is essential for the Ralph persistent evolutionary loop’s reliability guarantees.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →