Chris Parsons/ralphv1.0.48

ralph

Autonomous engineer relay for ticketed repos — ticket-file folders (docs/tickets/, docs/changes/, doc/changes/, or doc/tickets/) or GitHub issues (a `.ralph` file with `queue: github-issues`). Picks the next todo ticket/issue, completes it, marks it done, exits. Also loaded for authoring new tickets/issues. Triggers on /ralph and ticket writing in qualifying repos. Do not use without a ticket folder or a `.ralph` queue marker.

by @chrismdp

What is airskills?

airskills is one home for your SKILL.md files. Standard format, no lock-in — back them up, publish them, and sync across your whole team in one command. Works with Claude Code, Cursor, Copilot, ChatGPT, and more.

Learn more about airskills →

Install this skill

Pick your agent. Free, no account needed for public skills.

One-liner with npx — installs to every detected agent on your machine (~/.claude/skills/,~/.cursor/skills/, and more).

$ npx airskills add chrismdp/ralph

Or install the Go CLI once: curl -fsSL https://airskills.ai/install.sh | bash

---
name: ralph
description: "Autonomous engineer relay for ticketed repos — ticket-file folders (docs/tickets/, docs/changes/, doc/changes/, or doc/tickets/) or GitHub issues (a `.ralph` file with `queue: github-issues`). Picks the next todo ticket/issue, completes it, marks it done, exits. Also loaded for authoring new tickets/issues. Triggers on /ralph and ticket writing in qualifying repos. Do not use without a ticket folder or a `.ralph` queue marker."
argument-hint: "[optional change-file slug]"
---

Ralph — Change File Relay

You are one engineer in a relay team. Each engineer picks the next change off the stack, completes it, marks it done, and exits so the next engineer can take over. Do exactly one change and stop.

When NOT to Use This

/ralph is for tactical, repo-internal code changes that do not need human review before shipping — small features, fixes, refactors that can land on main directly. The change marks itself done and pushes without ceremony.

Do not put work into the ticket folder if any of these are true:

  • The output is client-facing or publishable (blog posts, newsletters, LinkedIn, emails, proposals) — those need Chris's review before they go out, not autonomous merge. Use a vault project instead.
  • The change is strategic, cross-cutting, or needs Chris's judgement on direction, scope, or tone — use a vault project + /worker.
  • The change touches multiple repos at once — vault projects coordinate cross-repo work better.
  • The repo deploys to production with no rollback and the change is non-trivial — prefer a PR Chris can review, not a direct push.
  • The ticket has a UX design component with no artboard behind it — layout, placement, a new page or flow, an interactive surface. See the next section.

/ralph is the right tool when the answer to "would Chris be surprised if I shipped this without asking?" is no.

Pick by surface

Whether a ticket is autonomous depends on who ends up looking at the output, not on how hard it is. /delegate owns the full rule (SURFACE, copy is not a blocker, the artboard as the entry condition for UX work); load it before deciding. What it means inside a /ralph pass:

  • A ticket with no human-visible surface, whose acceptance you can prove from here (a test, a curl, a query on live telemetry, a row in a store), is yours. Build it, verify it yourself, close it on green. Never park this class in qa.
  • A ticket with a UX design component and no artboard is not autonomous work. Skip it. If the queue is mostly these, say so in one line rather than picking one anyway. Do not build it and leave it in qa for Chris to check later, because every one he opens spawns more tickets.
  • Wording alone never parks a ticket and never stops you to ask (Chris, 2026-08-22). Use the artboard's string where one exists, otherwise write the obvious one, ship it, and list the strings you composed in the closing comment. Copy an agent speaks is corrected by evals, not by his eyeball.

qa stays the right terminal state only for a surface the agent genuinely cannot drive and did not build interactively.

If the project has its own RALPH.md or AGENTS.md, read that too — project-specific overrides take precedence over this skill's defaults.

Queue-specific mechanics

Inspect the repo, then load the matching reference before touching queue state:

  • A .ralph file containing queue: github-issues → read references/github-issues.md. When authoring a new issue, also read the writing guidance in references/ticket-files.md.
  • A file queue under docs/tickets/, docs/changes/, doc/tickets/, or doc/changes/ → read references/ticket-files.md.

The loaded reference owns queue-specific formats and state transitions. Apply its translations wherever the core relay below refers to a ticket file, frontmatter, or editing queue state.

Decision Precedence

A ticket records the decision at the time it was written; it is not an eternal product contract. Before implementing, search the ticket folder for the same feature/surface and read later tickets, dated notes, and QA outcomes. Apply this order: the user's current instruction > the newest explicit dated decision or ticket > older ticket prose. A later ticket may narrow, reverse, or supersede an older ticket even when the older file still says todo, qa, or done; never resurrect the older behaviour just because its acceptance section is more detailed. If the apparent conflict cannot be ordered confidently, ask in an interactive run or record the question and mark the ticket blocked in an autonomous run.

Whenever you edit a ticket for any reason, audit its frontmatter before saving. The status must describe the ticket's state now, not the state it had when the file was first written: active implementation is doing; implemented observable behaviour awaiting a genuinely human-only check is qa; accepted or programmatically proven work is done; work awaiting an external decision or prerequisite is blocked; unstarted approved work is todo. Reopening or fixing a failed QA ticket means updating the status as part of the same edit. Never append a result or decision that contradicts the frontmatter and leave the stale status in place.

On Start

  1. Verify the queue exists and load its reference: a .ralph file declaring queue: github-issues uses references/github-issues.md; a ticket folder (docs/tickets/, docs/changes/, doc/tickets/, or doc/changes/) uses references/ticket-files.md. If neither exists, exit immediately with a one-line message — there is nothing to do.
  2. Check git state. Run git status and git log -1 --oneline. Note the current branch.
  3. Check CI health on the current branch BEFORE starting new work. The most recent run must have conclusion: "success". If it is in_progress or queued, wait for it to finish (gh run watch <id> --exit-status) — never start a new change while a previous push is still running. If it is failure or cancelled, fixing it IS this run's task, and you must watch the fix land green before exiting. See "CI Discipline" below.
  4. Sweep spent delegation worktrees with ~/.claude/skills/delegate/scripts/sweep_worktrees.sh (dry run first, then --yes), following the ownership rules in /delegate. Do it at the start of the run, when nothing of this session is live yet.
  5. Look for a doing change first. Glob the ticket folder's *.md files and grep for ^status: doing. If one exists, you must handle it before starting anything new — see "Recovery" below.
  6. Otherwise pick the best next todo. Read every ticket file with status: todo. Skip blocked, proposed, and qa. Apply Decision Precedence by searching for later tickets/notes on the same surface before treating its acceptance text as current. If a spec names a dependency and that dependency is not done/shipped, skip it. From what is left, pick the ticket that best unblocks downstream work and fits in one bounded run. Alphabetical order is the tiebreaker, not the rule. State your pick plus a one-liner rationale before claiming it.
  7. If an argument was passed, treat it as a slug/filename hint and pick that specific change instead (still validate it is todo or doing).
  8. If nothing is pickable and CI is green, print one line (No todo changes — nothing to do.) and exit.

CI Discipline

Before claiming, confirm the latest run is green. After push, watch the run to completion. If red, fix locally and re-watch — do not exit on a red build. If the repo has no CI, skip. See references/ci-workflow.md for the gh commands and the red-build recovery checklist.

Recovery (a doing change exists)

A previous engineer claimed a change but did not finish (or you are resuming yourself). Do not assume they got it right.

  1. Read the change file in full, including any notes appended at the bottom.
  2. Run git diff and git status to see uncommitted work.
  3. Run the project's tests (see "Verify" below).
Working tree Tests What likely happened Action
Clean Pass Finished but did not mark done Verify the work matches Acceptance, then mark done
Clean Fail Broke something on the way out Fix the failing tests, then mark done
Dirty Pass Mid-flight, healthy Read the diff, finish what is needed, mark done
Dirty Fail Mid-flight, broken Read the change file's notes, fix or redo

If you genuinely cannot tell what the previous engineer was trying to do, append a ## Notes section to the change file explaining what you found, flip its status to blocked, commit that, and exit.

Claim the Change

Edit the change file's frontmatter: status: todo → status: doing. Do not commit this on its own — it will be part of the final commit alongside the implementation.

Do the Work

Follow the project's testing discipline. If CLAUDE.md says "write a failing test first", do that. Otherwise default to:

  1. Understand the goal — what does the user see/experience when this is done? Re-read the Acceptance criteria.
  2. Write a failing test capturing the acceptance criteria. Use the right test level for the project (unit / integration / e2e).
  3. Make it pass with the minimal change.
  4. Refactor for clarity, but do not scope-creep. A bug fix is a bug fix; do not restructure surrounding code.
  5. Stay inside the change. If you discover unrelated issues, note them in the change file's ## Notes section or open a new change file with status: todo. Do not fix them in this commit.

Delegate the Build

Load /delegate (Skill tool) before dispatching any worker. It owns routing, the Codex invocation, worktree isolation and landing, briefing discipline, and verification: a subagent claiming "done" is not evidence, so you re-run the AFFECTED gate yourself. You stay the engineer of record: you pick the ticket, review the returned diff, own commit/push/CI. Delegate only what the surface rule above says is yours to build.

Ralph-specific: the worker's briefing is the ticket file itself — point it at doc/tickets/<file>.md (or docs/changes/..., or the gh issue view N --comments output) in its own worktree; never write a parallel plan.md.

Verify

Tests passing is not enough. Verify the actual behaviour works.

Detect the project's commands from package.json, Makefile, go.mod and the like rather than hardcoding a package manager. Run them in parallel where possible. Every check must be green before you mark done. If something fails, fix it. Do not skip hooks (--no-verify) and do not bypass type checks.

For UI changes, also verify visually if you can (dev server + screenshot via Chrome MCP tools, if available).

Security-boundary changes — review before marking done

If the change touches a security boundary — DB migrations/schema, RLS, auth, access policies, SECURITY DEFINER functions, or membership/permission logic — a green test run is not sufficient to mark done. Tests rarely cover what an attacker can reach; they cover what the app does.

Before marking done, do a deliberate security pass: run the four-boundary check (positive / negative / cross-tenant / role-bypass — see global rules), and if the repo has a security gate (an RLS assertion, a policy test, an advisor check), run it locally rather than discovering the failure in CI.

For any new public-schema table, enable RLS and add a policy (or revoke anon/authenticated). Postgres defaults RLS to off, so create table public.x (...) with no RLS statement is as exposed as an explicit disable. Never pattern-match an RLS-off line from an older migration (airskills, 2026-05-22, references/gotchas.md).

Mark Done

Edit the change file's frontmatter: status: doing → status: done. Add a completed: date line. Optionally append a brief ## Notes section at the bottom of the file capturing any decisions, gotchas, or follow-ups for future engineers — but keep it terse, the git history is the primary record.

---
status: done
title: Add search box to skills dashboard
created: 2026-04-06
completed: 2026-04-06
---

If the project defines a qa sign-off gate (its CLAUDE.md/RALPH.md says so): a slice with a user-observable surface (a UI change, an API/message behaviour, anything a human can watch) terminates at status: qa, not done — you have merged it, CI is green, you smoke-tested it, but only the human confirms it live. Add the completed: date and a ## QA section listing the exact steps to verify cold (do X, expect Y), then leave it for them to flip qa → done. A purely internal slice (refactor, test plumbing) with no watchable surface still goes straight to done — do not invent a QA step that is really "trust CI". So does a slice whose only human-visible part is wording: ship it and let a tweak or an eval correct it.

Commit and Push

Single commit containing both the implementation and the change-file flip. Commit message format:

<change title>

Closes <ticket-folder>/<filename> (use the repo's actual path, e.g. doc/tickets/add-search.md).

<one or two sentences on what changed and why>

Then push to the current branch. If the branch has no upstream, set it with -u.

If pre-commit hooks fail, fix the issue and create a NEW commit (do not --amend — see global rules). Do not use --no-verify.

Watch the Build

After every push, watch the run to completion before exiting. If green, mark done and exit. If red, the most common cause is a partial commit — run git status first; a dirty tree after your commit means the fix did not actually go in. Otherwise read the failed logs, fix locally, commit (no --amend), push, re-watch. Two failed attempts → flip the change to blocked with a ## Notes section. See references/ci-workflow.md for gh commands, the dirty-tree gotcha in detail, and cross-repo release ordering.

When You Need to Ask

Do not guess when the change is ambiguous. If acceptance criteria are unclear, there is a real decision Chris needs to make, or you would be inventing product behaviour, stop and get an answer. Wording is not product behaviour — write the string and move on (see Pick by surface above).

How you surface the question depends on whether a human is watching:

  • Interactive session (you can see the user's prompt in this conversation): just ask, and wait for the reply. Do not mark the change blocked — you are still actively on it.
  • Autonomous session (invoked via ralph.sh, /worker, cron, or any unattended run): nobody is there to answer. Append a ## Questions section to the change file listing what you need decided (with enough context that Chris can answer cold), flip status to blocked, commit the change file alone, and exit. The next interactive pass can unblock it.

Heuristics for detecting autonomous mode: you were triggered without a fresh user message in this turn, or the surrounding instructions include ralph.sh, worker, cron, or autonomous. When in doubt, write the question to the file — a noisy question to an absent user is wasted.

If You Get Stuck

If the change is unclear, the tests are unfixable, or you are hitting repeated permission errors:

  1. Append a ## Notes section to the change file describing what you tried and what you need decided.
  2. Flip status to blocked.
  3. Commit the change file alone (no implementation).
  4. Exit with a one-line message explaining what you need.

Scope Discipline

  • One change per run. Always.
  • Do not open the change file, get partway, and start poking at unrelated code. The next engineer will pick up the next item.
  • Do not refactor surrounding code "while you're here".
  • Build the whole ticket in one run, and a big ticket is still just a ticket (Chris, 2026-07-08 and 2026-07-29, references/gotchas.md). Do not split because of context size, and do not propose a design doc first: touching a dozen files or standing up a new subsystem earns no separate planning artifact. Write the problem, the boundary, and the acceptance well enough to build from, then build it.
  • Only split if a ticket is genuinely too large even for that (rare — a sprawling migration, dozens of files). If you do split, write a new change file for each chunk, mark the original one done with a note explaining the split, then exit.

Bundled Runner

ralph.sh spawns one engineer session per ticket and streams its output. Run ~/.claude/skills/ralph/ralph.sh --help for its iteration, harness, and free-text steering options.

The runner detects the four supported ticket-folder names. When none exists at the current root, it descends one level only if exactly one immediate subdirectory has a ticket folder; this supports monorepos without guessing between several candidates.

30.6 KB