Skip to content

Hats & Events

Hats are specialized Ralph personas that coordinate through typed events. This enables complex workflows with role separation.

What Are Hats?

A hat is a persona that Ralph can "wear" — each with:

  • Triggers — Events that activate this hat
  • Publishes — Events this hat can emit
  • Instructions — Prompt injected when hat is active
hats:
  planner:
    name: "📋 Planner"
    triggers: ["task.start"]
    publishes: ["plan.ready", "plan.blocked"]
    instructions: |
      Create an implementation plan for the task.
      When done, emit plan.ready with a summary.

How Events Work

Events are typed messages with:

  • Topic — What kind of event (e.g., build.done)
  • Payload — Optional data
  • Source hat — Which hat published it
  • Target hat — Optional routing

Event Flow

flowchart LR
    task.start --> Planner
    Planner -->|plan.ready| Builder
    Builder -->|build.done| Reviewer
    Reviewer -->|review.approved| LOOP_COMPLETE

Publishing Events

Hats publish events using ralph emit:

ralph emit "build.done" "tests: pass, lint: pass, typecheck: pass, audit: pass, coverage: pass"

Or with JSON payloads:

ralph emit "review.done" --json '{"status": "approved", "issues": 0}'

Event Routing

Events are routed to hats based on subscription patterns:

Exact Match

triggers: ["task.start"]  # Only matches "task.start"

Glob Patterns

triggers: ["build.*"]     # Matches build.done, build.failed, etc.
triggers: ["*.error"]     # Matches build.error, test.error, etc.
triggers: ["*"]           # Matches everything

Hat Configuration

Basic Hat

hats:
  builder:
    name: "🔨 Builder"
    triggers: ["task.start", "plan.ready"]
    publishes: ["build.done", "build.failed"]
    instructions: |
      Implement the task or plan.
      Run tests before declaring done.

Hat with Backend Override

hats:
  reviewer:
    name: "🔍 Reviewer"
    triggers: ["build.done"]
    publishes: ["review.approved", "review.rejected"]
    backend: "claude"  # Use Claude even if default is different
    instructions: |
      Review the implementation for quality.

Hat with Max Activations

hats:
  refactorer:
    name: "✨ Refactorer"
    triggers: ["test.passed"]
    publishes: ["refactor.done"]
    max_activations: 3  # Limit how many times this hat activates
    instructions: |
      Clean up the code.

Default Publishes

hats:
  worker:
    triggers: ["task.start"]
    publishes: ["work.done", "work.blocked"]
    default_publishes: "work.done"  # If no explicit emit

Event System Design

Starting Event

The first event published when Ralph starts:

event_loop:
  starting_event: "task.start"  # Triggers initial hat

Completion Promise

The signal that ends the loop:

event_loop:
  completion_promise: "LOOP_COMPLETE"

A hat can output this directly, or emit a completion event:

hats:
  coordinator:
    triggers: ["all.done"]
    instructions: |
      All work complete. Output: LOOP_COMPLETE

Common Patterns

Pipeline

Linear flow from one hat to the next:

flowchart LR
    A[Planner] -->|plan.ready| B[Builder]
    B -->|build.done| C[Tester]
    C -->|test.passed| D[Deployer]

Supervisor-Worker

One coordinator, multiple workers:

flowchart TB
    S[Supervisor] -->|task.a| A[Worker A]
    S -->|task.b| B[Worker B]
    A -->|done.a| S
    B -->|done.b| S

Critic-Actor

One proposes, another critiques:

flowchart LR
    A[Actor] -->|proposal| C[Critic]
    C -->|approved| Done
    C -->|rejected| A

Viewing Events

# View event history
ralph events

# Output:
# 2024-01-21 10:30:00 task.start → planner
# 2024-01-21 10:32:15 plan.ready → builder
# 2024-01-21 10:35:42 build.done → reviewer

Best Practices

1. Keep Events Small

Events are routing signals, not data transport:

# Good: Small payload
ralph emit "build.done" "tests: pass, lint: pass, typecheck: pass, audit: pass, coverage: pass"

# Bad: Large payload
ralph emit "build.done" "full output of all test results..."

Use memories for detailed output:

ralph tools memory add "Build details: ..." -t context
ralph emit "build.done" "tests: pass, lint: pass, typecheck: pass, audit: pass, coverage: pass"

2. Clear Triggers

Make triggers specific:

# Good: Specific
triggers: ["plan.ready", "plan.revised"]

# Risky: Too broad
triggers: ["*"]

3. One Responsibility Per Hat

Each hat should have a clear, single purpose:

# Good: Focused
hats:
  tester:
    triggers: ["build.done"]
    instructions: "Run tests and report results."

# Bad: Multiple responsibilities
hats:
  do_everything:
    triggers: ["*"]
    instructions: "Test, lint, deploy, document..."

Next Steps