Loading...
Loading...
Author a beads-backed Ralph Wiggum loop for in-session or external autonomous execution.
npx skill4agent add jimweller/clanker-skills ralph-builderbd.llmtmp/| Path | Purpose |
|---|---|
| Beads database (single source of task state) |
| Mode-neutral execution plan |
| Slash-command wrapper for the in-session ralph-loop plugin |
| Body fed to |
README.md.md.llmdocs/.llmtmp/--parentif ! bd list >/dev/null 2>&1; then
bd init --skip-agents
fi
slug=$(git branch --show-current | tr '/' '-')
epic=$(bd q "Ralph: ${slug}" -t epic -p 1 -l "ralph:${slug}")--skip-agentsAGENTS.mdCLAUDE.md.claude/settings.jsonbeadsralph:${slug}--parent "${epic}"bd create -t task --parent "${epic}" -l "ralph:${slug}" -q --json | jq -r .idbd linkbd update <id> -d "<text>"--body-file -bd link <a> <b>lib/greeting.goSLUGCLAUDE.mdralph-plan.mdDATE=$(date +%Y%m%d)
SLUG=greeting # derive from the goal; lowercase alphanumeric plus hyphen, max ~20 chars
HASH=$(openssl rand -hex 2 | head -c 3)
RUN="${DATE}-${SLUG}-${HASH}"
BRANCH="ralph/${RUN}"
# slug and epic are in scope from Step 2.
bdc() {
bd create "$1" -t task -p 2 --parent "${epic}" -l "ralph:${slug}" -q --json | jq -r .id
}
T_BRANCH=$(bdc "Switch to branch ${BRANCH}")
bd update "$T_BRANCH" -d "Switch to branch ${BRANCH}, creating it if missing. Verify: git rev-parse --abbrev-ref HEAD prints ${BRANCH}."
T_BEGIN=$(bdc "Tag RALPH-${RUN}-BEGIN")
bd link "$T_BEGIN" "$T_BRANCH"
bd update "$T_BEGIN" -d "Run: git tag RALPH-${RUN}-BEGIN. Verify: git tag -l RALPH-${RUN}-BEGIN is non-empty."
T1=$(bdc "Add hello() to lib/greeting.go")
bd link "$T1" "$T_BEGIN"
bd update "$T1" --body-file - <<'DESC'
TDD workflow: write failing tests first, then implement minimum code to pass.
RED phase (write first):
Create lib/greeting_test.go. Test cases: hello() returns "world".
GREEN phase (then implement):
Create hello() in lib/greeting.go returning "world".
VERIFY: go test ./lib/... passes. Commit: feat(greeting): add hello
DESC
T2=$(bdc "Add goodbye() to lib/greeting.go")
bd link "$T2" "$T_BEGIN"
bd update "$T2" --body-file - <<'DESC'
TDD workflow: write failing tests first, then implement minimum code to pass.
RED phase (write first):
Add to lib/greeting_test.go. Test cases: goodbye() returns "bye".
GREEN phase (then implement):
Create goodbye() in lib/greeting.go returning "bye".
VERIFY: go test ./lib/... passes. Commit: feat(greeting): add goodbye
DESC
T3=$(bdc "Add greet(name) to lib/greeting.go")
bd link "$T3" "$T1"
bd link "$T3" "$T2"
bd update "$T3" --body-file - <<'DESC'
TDD workflow: write failing tests first, then implement minimum code to pass.
RED phase (write first):
Add to lib/greeting_test.go. Test cases: greet("Jim") returns
"world Jim bye".
GREEN phase (then implement):
Create greet(name) in lib/greeting.go composing hello() and goodbye().
VERIFY: go test ./lib/... passes. Commit: feat(greeting): add greet
DESC
T4=$(bdc "Document greetings API")
bd link "$T4" "$T3"
bd update "$T4" -d "Run the docs skill (/docs, \$docs) to document hello(), goodbye(), and greet() now exposed by lib/greeting.go. Commit: docs: document greetings API"
T_END=$(bdc "Tag RALPH-${RUN}-END")
bd link "$T_END" "$T4"
bd update "$T_END" -d "Run: git tag RALPH-${RUN}-END. Verify: git tag -l RALPH-${RUN}-END is non-empty."bd dep cycles
bd list --parent "${epic}" --json | jq -e 'all(.[]; (.description // "") | length > 0)'
bd ready --parent "${epic}" --json | jq 'length'
parent_ids=$(bd list --parent "${epic}" --json | jq -r '.[].id' | sort)
label_ids=$(bd list --label "ralph:${slug}" --json | jq -r '.[] | select(.issue_type != "epic") | .id' | sort)
[[ "${parent_ids}" == "${label_ids}" ]] || { echo "parent/label set mismatch: orphans or misclassified beads"; exit 1; }CLAUDE.mdralph-plan.md| Work | Skill |
|---|---|
Updating | docs (/docs, $docs) |
Run the docs skill to document the new auth middlewareEdit .llmdocs/architecture.md to add an Authentication subsection.llmtmp/ralph-plan.md${slug}${epic}plan documents:# Ralph Plan
## Goal
<one paragraph>
## Approach
<strategy specific to this goal; include all relevant repo-specific detail directly>
## Inputs
plan documents: <comma-separated paths from the user's arguments, or empty if none>
## Run Identity
- Branch slug: `<slug from Step 2>`
- Epic ID: `<epic ID from Step 2>`
- Epic label: `ralph:<slug>`
- Driver: `scripts/ralph.sh .llmtmp/ralph-prompt-external.md`
## Conventions
<repo-specific patterns, tools, test commands, build commands, commit style>
## Git Workflow
All git operations execute as bd tasks per the per-task workflow. Branch creation, BEGIN tag, code commits, docs commits, and END tag are first-class beads in the graph, parented to the run epic. Never commit on `main`.
## Per-Task Workflow
Exactly ONE task per iteration. After step 5, follow the driver instruction. The driver controls whether the agent loops in-session or stops for a fresh context.
Each iteration:
1. Read the epic ID and slug from the "Run Identity" section above.
2. Run `bd ready --parent <epic-id> --json`. If the result is `[]`, run `bd close <epic-id> -m "ralph run complete"`, output `<promise>ALLDONE</promise>`, and stop.
3. Pick the first task. Run `bd show <id>` to read the title and description.
4. Execute the description exactly. The description names the files, commands, verification step, and commit message for this task.
5. Run `bd close <id>`. Follow the driver instruction below.
The sentinel `<promise>ALLDONE</promise>` is emitted only on the iteration where step 2 finds the ready queue empty (all children closed and the epic just closed).
## Driver Instruction
<!-- Replaced at plan-write time by the prompt wrapper. -->
<!-- In-session: "Return to step 1 to pick the next task." -->
<!-- External: "STOP. The driver spawns a fresh context for the next task." -->plan documents:README.mdCLAUDE.md.llmdocs/*.llmtmp/Read .llmtmp/ralph-plan.md and follow it. After closing each task, return to step 1 of the Per-Task Workflow to pick the next ready task. Do not stop between tasks.Read .llmtmp/ralph-plan.md and follow it. After closing each task, STOP. The driver spawns a fresh context for the next task..llmtmp/ralph-prompt-insession.md/ralph-loop:ralph-loop "<in-session body>" --max-iterations 100 --completion-promise ALLDONE.llmtmp/ralph-prompt-external.mdbd list >/dev/null 2>&1bd dep cyclesbd list -t epic -l "ralph:${slug}" --json | jq -e 'length == 1'bd ready --parent "${epic}" --json | jq 'length'bd list --parent "${epic}" --json | jq -e 'all(.[]; (.description // "") | length > 0)'diff <(bd list --parent "${epic}" --json | jq -r '.[].id' | sort) \
<(bd list --label "ralph:${slug}" --json | jq -r '.[] | select(.issue_type != "epic") | .id' | sort).llmtmp/ralph-plan.md.llmtmp/ralph-prompt-insession.md.llmtmp/ralph-prompt-external.md