Ralph Loop

SkillAI & models

Run a high-thoroughness end-to-end implementation loop using clean worker worktrees, per-slice planning, implementation, explicit review/fix iteration, orchestrator integration, and final commit/PR drafting. Use when the user explicitly wants a Ralph/Wiggum-style loop, autonomous multi-agent execution, a delegated feature/bugfix carried from confirmed scope through review-ready completion, or RFC-driven implementation that should be reviewed, bumped to In Progress, decomposed into phases, and executed through nested sub-loops. Supports Codex subagents by default and external CLI workers such as OpenCode when explicitly requested and available.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Ralph Loop skill

What this skill tells your AI

The instructions your AI receives, as published by encero-systems/incan in .agents/skills/ralph-loop/SKILL.md and read by ahel’s review.

Use this skill when the user wants more than basic delegation. The goal is not "parallelize some work"; the goal is to carry a scoped task through planning, implementation, review, integration, and ship-ready artifacts with explicit backpressure.

Keep orchestrate-parallel-work generic. Use this skill as the opinionated wrapper around it.

Core stance

  • Confirm the requested scope before spawning anything.
  • If the input is an RFC, treat RFC hygiene and lifecycle state as part of the work, not pre-existing setup.
  • Treat user-facing docs and versioning as part of implementation, not optional closeout polish.
  • Optimize for end-to-end correctness, not maximal concurrency.
  • Use clean worktrees for real implementation slices.
  • For milestone, RFC-wide, or compiler-boundary work, define the acceptance contract before implementation starts. The contract should name required boundary parity coverage, downstream acceptance lanes, docs/generated-reference gates, performance/progress gates, and any criteria that must be satisfied before an RC or publish step.
  • For language or stdlib work that is meant to be implemented in Incan, dogfood Incan rather than creating a thin .incn facade over a custom Rust backend. Direct from rust:: imports of existing crates/primitives are acceptable when the .incn module still owns the behavior; bespoke incan_stdlib::feature Rust modules that hide the feature logic are not acceptable unless the maintainer explicitly asks for that backend boundary.
  • Do not let workers assume Incan cannot express something. Before choosing Rust/backend fallback or narrowing a design, workers must inspect current .incn precedents, parser/typechecker/codegen tests, or run a small probe and record the specific capability evidence.
  • Every implementation must land in a fresh worktree rooted under WORKTREE_ROOT so VS Code picks it up. WORKTREE_ROOT must resolve to the primary repository's shared sibling tmp/ directory; do not implement in the system /tmp, /private/tmp, or in the main repo checkout.
  • Treat Ralph-created worktrees, Cargo targets, generated SDK/provider caches, test-package output, and tool distributions as one managed storage domain. A worker may not create a durable build/output directory outside that domain.
  • Treat each slice as a managed work packet with durable state on disk, not as a chat thread that has to remember its own scope.
  • Treat loop identity as durable state too. A resumed loop must prove that persisted state matches the current user request before using it to choose work.
  • Treat cross-repo consumer work as a verification lane unless the user explicitly puts that repository's implementation work in scope.
  • Require every worker to plan, implement, verify, review, and fix within its owned slice.
  • Consolidate accepted worker output onto the orchestrator branch before any commit, push, or PR work. Worker worktrees are temporary execution sandboxes, not final publication branches.
  • Retire worker worktrees after their done output has been integrated and verified unless the orchestrator records a concrete reason to keep one.
  • Require the orchestrator to perform its own explicit plan -> do -> check -> act integration loop after collecting worker output.
  • Require workers and the orchestrator to maintain a persistent review report at .agents/state/review-report.md inside each worktree so findings survive across loops.
  • Require touched .incn source to pass review-incan-source-quality; Incan implementation work is not done when it merely compiles if the authored source reads like Rust-shaped scaffolding instead of well-written Python.
  • Do not commit, push, or open a PR unless the user explicitly asked for that. When not explicitly asked, still draft the commit message and PR description as ready-to-use artifacts.

PDCA model

ralph-loop is a plan -> do -> check -> act loop, not a linear pipeline.

Use these meanings consistently:

  • Plan: confirm scope, define slices, create task state, decide the next concrete increment
  • Do: implement the planned increment in the owned slice
  • Check: verify behavior, run review, compare delivered work against the requested scope
  • Act: either accept and move forward, repair findings, or go back to planning when scope is not actually satisfied

Important:

  • Review findings that are simple defects belong in Act -> fix.
  • Evidence that the slice or integration output does not satisfy the intended scope belongs in Act -> re-plan.
  • Do not treat “missing promised behavior” as just another lint item. That is a planning/state problem first.

Scope and state isolation

Every Ralph loop must have a persisted loop identity before workers are spawned or resumed. Record it in the orchestrator overview and use it to filter durable state:

  • loop_id: stable slug for this run, usually <repo>-<issue-or-rfc-or-milestone>-<short-purpose>
  • target repository or repositories
  • primary implementation repository
  • base branch or resolved starting commit
  • issue/RFC/milestone allowlist
  • explicit non-goals
  • permitted downstream or consumer verification lanes
  • whether cross-repo implementation is allowed

Use STATE_ROOT to mean the active loop's state directory. For new loops, prefer a namespaced root:

.agents/state/ralph-loop/<loop-id>/

Legacy top-level state such as .agents/state/ralph-loop/slices.json may be read for migration or handoff, but must not silently drive a new loop unless its recorded loop identity matches the current request.

On every resume, stop-hook continuation, compaction, or long-running restart:

  1. Re-read the latest user instruction.
  2. Re-read STATE_ROOT/overview.md, STATE_ROOT/slices.json, and .agents/state/review-report.md.
  3. Compare persisted loop identity to the latest user instruction.
  4. Classify each slice as:
    • in_scope: same loop identity and allowed implementation repo
    • verification_lane: downstream/consumer repo explicitly named as validation only
    • out_of_scope: different repo, issue, milestone, branch, or objective
  5. Schedule only in_scope implementation slices.

Out-of-scope or historical slices may be cited as background evidence, but they must not be advanced, committed, pushed, or turned into PRs from the current loop. If the user wants to pick them up, start or resume a separate Ralph loop with its own loop identity.

Consumer repositories such as IncQL may be used to reproduce, verify, or acceptance-test an Incan compiler/runtime change only when listed as a verification lane. Adding product features, docs CI, repo automation, or cleanup in that consumer repo is a separate implementation loop unless the user explicitly asks for it.

When not to use this skill

Do not use this for:

  • a small task where one agent can finish faster than the orchestration overhead
  • a tightly coupled design problem with one unresolved architectural decision
  • multiple workers that would need to edit the same files or shared API surface
  • tasks where the user wants a plan only

Workflow

0. RFC intake mode

If the user feeds the loop an RFC, enter RFC intake mode before ordinary execution.

In RFC intake mode:

  1. Run review-rfc on the RFC and apply direct fixes.
  2. Inspect Status: and the design tail honestly.
  3. If unresolved questions remain, summarize them and ask the user before bumping anything.
  4. Check for process blockers that require user input, such as:
    • external dependencies not yet available
    • another RFC or issue that partially blocks the scope
    • scope that should be split before implementation
  5. If the RFC is still Draft and design is settled, use bump-rfc to move it to Planned.
  6. If implementation is actually being picked up now, use bump-rfc to move it to In Progress. For this skill, the parent Ralph loop itself counts as "a contributor has picked up the work"; a child loop does not need its own PR or branch-level ceremony.
  7. Use the RFC's Implementation Plan and Progress Checklist as the source of truth for implementation phases. If they are missing or weak, strengthen them before spawning workers.
  8. Establish the docs/version baseline up front: verify the repo's actual dev version from the source-of-truth metadata, identify which user-facing docs must change if the feature lands, and do not assume an older release line from stale release notes or worker worktrees.

Do not quietly force an RFC past open design questions. Stop and ask the user.

1. Confirm the scope

Restate the requested end-state before starting execution. Confirm:

  • target repository or repositories
  • primary implementation repository
  • issue / RFC / branch context
  • milestone allowlist, if milestone-driven
  • explicit goals
  • explicit non-goals
  • downstream repositories that are verification lanes only
  • cross-repo implementation permission, if any
  • whether the task is RFC-wide or only a subset of RFC phases
  • whether commit / push / PR creation were requested
  • whether the user wants Codex subagents or an external backend such as OpenCode

If scope is ambiguous, stop and ask a short numbered list of missing decisions.

After confirming scope, write the loop identity into STATE_ROOT/overview.md before creating or resuming slices. If existing durable state lacks a matching loop identity, do not continue it as part of this loop.

2. Pick the execution backend

Default to Codex subagents plus git worktrees under WORKTREE_ROOT.

Resolve WORKTREE_ROOT once at the start of the loop and record its absolute path in STATE_ROOT/overview.md. It is the primary repository's shared sibling tmp/ directory. RALPH_WORKTREE_ROOT is only an assertion of that location: if it resolves elsewhere, stop rather than silently accepting another root. In particular, never use the system temporary directory as a worktree or durable build-output root.

COMMON_GIT_DIR="$(git rev-parse --path-format=absolute --git-common-dir)"
WORKSPACE_ROOT="$(dirname "$(dirname "$COMMON_GIT_DIR")")"
EXPECTED_WORKTREE_ROOT="$WORKSPACE_ROOT/tmp"
mkdir -p "$EXPECTED_WORKTREE_ROOT"
EXPECTED_WORKTREE_ROOT="$(cd "$EXPECTED_WORKTREE_ROOT" && pwd -P)"
if [ -n "${RALPH_WORKTREE_ROOT:-}" ]; then
  [ -d "$RALPH_WORKTREE_ROOT" ] || {
    echo "RALPH_WORKTREE_ROOT must already be $EXPECTED_WORKTREE_ROOT" >&2
    exit 2
  }
  [ "$(cd "$RALPH_WORKTREE_ROOT" && pwd -P)" = "$EXPECTED_WORKTREE_ROOT" ] || {
    echo "RALPH_WORKTREE_ROOT must be $EXPECTED_WORKTREE_ROOT" >&2
    exit 2
  }
fi
WORKTREE_ROOT="$EXPECTED_WORKTREE_ROOT"

git-common-dir keeps the default stable when the loop runs from a linked worktree.

Before creating a worktree, and before every command that can perform a substantial build, enforce the storage boundary:

  1. Record du -sk "$WORKTREE_ROOT", df -k "$WORKSPACE_ROOT", and the primary repository's git worktree list --porcelain inventory in STATE_ROOT/storage-ledger.md.
  2. Record every Ralph-owned output path in that ledger, including CARGO_TARGET_DIR, generated SDK/provider caches, package/distribution output, and test fixtures that copy repositories or build trees.
  3. Put each such path under WORKTREE_ROOT, normally under a loop-specific .ralph-cache/<loop-id>/ directory. Do not rely on a tool's default temporary path.
  4. Treat 20 GiB as a hard combined ceiling for all Ralph-owned material in WORKTREE_ROOT. If the ledger or measured root is at the ceiling, do not start another build; first retire an already-integrated Ralph worktree/cache, or report a concrete blocker. Never solve this by moving output to /tmp or /private/tmp.

For every build/test command, set CARGO_TARGET_DIR and TMPDIR to that slice's owned locations. Set any project-specific output variables too (for example, TOOLCHAIN_DIST for Incan's release-toolchain smoke commands). The ledger must distinguish retained worktrees from reclaimable targets/caches and must name the owner slice. Any primary-repository worktree outside WORKTREE_ROOT is legacy or foreign and must be explicitly recorded; a Ralph-owned one is a cleanup blocker, not a reason to create another outside-root worktree. git worktree remove alone is not cleanup when a worker placed output in a separate cache directory.

If the user explicitly wants OpenCode:

  • verify opencode is installed and callable
  • verify auth / provider setup is present enough to run unattended
  • prefer non-interactive execution such as opencode run ... or a preconfigured OpenCode agent profile
  • do not rely on the TUI for unattended worker execution

If OpenCode is requested but unavailable, say so plainly and either stop or fall back to Codex only with the user's approval.

3. Prepare the orchestration boundary

Use start-work once at the orchestration boundary to resolve issue/RFC context, branch naming, and relevant learnings.

Do not mechanically run start-work inside every worker unless each worker owns a distinct issue or RFC. The important requirement is a clean worktree plus resolved context, not duplicated branch ceremony.

Create WORKTREE_ROOT first if it does not exist.

Create:

  • one orchestrator worktree for final integration
  • one clean worker worktree per implementation slice

Create durable slice state under each worktree. The paths below are relative to STATE_ROOT; for new loops STATE_ROOT should normally be .agents/state/ralph-loop/<loop-id>:

  • overview.md for orchestrator-wide state, including loop identity and scope boundaries
  • STEERING.md for operator overrides, urgency changes, and mid-run guidance
  • slices.json for the orchestrator-owned slice registry
  • <slice-id>/scope.md
  • <slice-id>/plan.md
  • <slice-id>/tasks.json
  • <slice-id>/tasks.md
  • <slice-id>/status.md
  • <slice-id>/handoff.md

Treat the slice folder as the source of truth for that slice. Do not rely on memory or only on conversational context.

Use these files deliberately:

  • STEERING.md: human or orchestrator overrides that should preempt normal task ordering
  • slices.json: orchestrator registry of all active slices, their owners, and their current state
  • tasks.json: machine-readable task list for the slice
  • tasks.md: human-readable task narrative and notes
  • status.md: current PDCA state, blockers, and next action

Base all worker worktrees from the same resolved starting point unless there is a deliberate dependency chain.

For non-decomposed work, the single-agent implementation still belongs in a fresh worktree under WORKTREE_ROOT; "keep the work local" does not mean "edit the primary checkout directly."

Before spawning workers, identify:

  • the source-of-truth version file(s) for the repo
  • whether the task is on a -dev.N line and therefore needs a version bump
  • the authored user-facing docs that must be updated if the change is user-visible

For milestone, RFC-wide, or compiler-boundary work, also write an ## Acceptance Contract section into STATE_ROOT/overview.md before implementation starts. Include:

  • the local/direct behavior that must work,
  • import, reexport/facade, package-consumer, test-batch, dependency, vocab, or generated-Rust boundaries that can observe the behavior,
  • downstream acceptance lanes such as IncQL when the surface is exercised there,
  • performance or progress-output expectations when the change touches prewarm, test runner, metadata, or CI paths,
  • docs, rustdocs, generated-reference, release-note, or RFC lifecycle gates,
  • explicit non-applicable boundaries, with a short reason.

Do not defer this acceptance contract to release-branch closeout. The release branch should verify the contract, not discover it.

Do not treat RFC edits or release notes alone as sufficient user documentation.

4. Decide whether parallelism is justified

Before spawning workers, decide whether the task actually decomposes cleanly. If it does not, keep the work local and continue as a single-agent Ralph loop.

When it does decompose, hand off to orchestrate-parallel-work for slice definition, ownership, and worktree isolation under WORKTREE_ROOT. Pass the resolved WORKTREE_ROOT, loop-specific .ralph-cache/<loop-id>/ root, and 20 GiB ceiling as mandatory inputs; the delegated orchestrator and workers must not recompute or override their own temporary root.

If the task came from an RFC:

  • derive slices from RFC implementation phases or coherent checklist groupings, not arbitrary percentages
  • keep parent ownership of RFC lifecycle edits, progress-checklist updates, commit text, and PR drafting
  • treat child loops as implementation subloops only
  • allow nested decomposition only one level down: child loops may use orchestrate-parallel-work for leaf workers inside their owned scope, but they must not spawn further ralph-loop children

For each slice, create tasks.md with explicit task items. Keep them concrete and finishable, not vague “phase done” markers.

Also create tasks.json for the same slice. tasks.json is the authoritative status surface; tasks.md is the human-readable companion.

Suggested shape:

{
  "slice_id": "lifecycle-env",
  "status": "planned",
  "tasks": [
    {
      "id": "T1",
      "title": "Establish manifest/env schema ownership boundary",
      "status": "todo",
      "notes": ""
    },
    {
      "id": "T2",
      "title": "Remove CLI-local env schema parsing",
      "status": "todo",
      "notes": ""
    }
  ]
}

Example shape:

# Slice Tasks — lifecycle-env

- [ ] Establish manifest/env schema ownership boundary
- [ ] Remove CLI-local env schema parsing
- [ ] Add internal manifest override discovery
- [ ] Make env overlays affect nested `incan lock`
- [ ] Add focused tests
- [ ] Run slice verification
- [ ] Run slice review/fix loop

Subagents should work tasks to completion against these files and update them as state changes.

The orchestrator should maintain slices.json with entries like:

[
  {
    "loop_id": "incan-557-std-environ",
    "slice_id": "lifecycle-env",
    "repo": "encero-systems/incan",
    "scope_role": "implementation",
    "owner": "<worker name or id>",
    "worktree": "<WORKTREE_ROOT>/...",
    "status": "doing",
    "next_action": "Implement T2"
  }
]

Use scope_role: "verification" for downstream acceptance lanes. Verification slices may run checks and collect evidence, but they must not mutate consumer product code unless the loop identity explicitly allows cross-repo implementation.

5. Give each worker a strict end-to-end contract

Each worker must own a non-overlapping slice with:

  • loop identity
  • repository and scope role (implementation or verification)
  • exact goal
  • owned files or directories
  • explicit non-goals
  • forbidden repositories or paths
  • dedicated worktree path under WORKTREE_ROOT
  • dedicated target/cache/output paths under WORKTREE_ROOT/.ralph-cache/<loop-id>/<slice-id>/, with the owner recorded in STATE_ROOT/storage-ledger.md
  • dedicated slice folder under STATE_ROOT/<slice-id>/
  • verification command
  • expected result format

For RFC-driven work, prefer one child loop per implementation phase or tightly related checklist group.

Each worker must perform this loop inside its slice:

  1. Plan
    • Build a short slice plan using create-plan.
    • For .incn, stdlib, or language-surface work, perform capability intake before ruling out an Incan-native design. Record the local precedent, test, or probe result in plan.md.
    • Write scope.md, plan.md, tasks.json, and tasks.md.
    • Break the slice into concrete tasks inside tasks.json and keep tasks.md as the readable companion.
    • Set initial slice state in status.md as planned.
  2. Do
    • Implement the next planned task set, not the whole universe.
    • Update tasks.json, tasks.md, and status.md as task ownership and progress change.
    • Use doing as the active execution state.
  3. Check
    • Run targeted verification for the slice.
    • Run review-incan-source-quality when the slice touches .incn source.
    • Run review on the slice in report-only mode, or review-orchestrate if the slice itself is broad enough to justify specialization.
    • Compare the delivered behavior against scope.md, not only against test output.
    • Use checking as the active verification/review state.
  4. Act
    • Run fix on every actionable in-scope blocker or warning.
    • If the slice hits a likely compiler bug that is out of scope, invoke flag-compiler-bug before reporting the blocker or choosing a workaround.
    • If check/review shows that promised scope is still not satisfied, change slice state to replan_required and go back to Plan. Update scope.md, plan.md, tasks.json, and tasks.md before doing more code.
    • If outside help is needed, set slice state to blocked with a concrete blocker.
    • If the slice is actually complete, set slice state to done and write handoff.md.
  5. Repeat until:
    • no actionable in-scope items remain,
    • the slice tasks are complete,
    • and scope.md is honestly satisfied, or report a concrete blocker with its classification.

The slice's .agents/state/review-report.md must be kept current throughout this loop. The slice folder under STATE_ROOT/<slice-id>/ must also stay current throughout this loop.

Allowed slice states:

  • planned
  • doing
  • checking
  • replan_required
  • blocked
  • done

Do not invent vague alternatives like “mostly done” or “almost ready”.

Workers must be told:

  • they are not alone in the repo
  • they must not revert or overwrite others' work
  • they must adapt to concurrent changes
  • they must not produce PRs, PR descriptions, or final commit artifacts of their own when they are child loops under a parent RFC loop
  • child loops must not spawn further ralph-loop children; if they need extra decomposition, they may use orchestrate-parallel-work only for leaf-level workers within their owned scope
  • they must not commit or push unless the user explicitly asked for that

Require every worker to return the shape in reference.md.

6. Integrate centrally

The orchestrator owns integration. Workers do not integrate each other.

The orchestrator must:

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
228
Forks
70
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
ralph-loop-encero-systems
Source
github.com/encero-systems/incan