Skip to content

Commit fb93dca

Browse files
committed
feat(run): inject next unchecked task into loop context (#118)
Extract the first unchecked `- [ ]` line from @fix_plan.md and inject it as `"Next: <task>."` in the loop context string, giving the AI a clear directive instead of re-parsing the full fix plan each iteration. - Add extract_next_fix_plan_task() helper (grep -m 1, trimmed, 100-char cap) - Inject "Next:" segment in build_loop_context() after "Remaining tasks:" - Update PROMPT.md to reference loop context task instead of "choose" - Add 9 tests (6 helper + 3 injection) Closes #118
1 parent 77bb7f3 commit fb93dca

3 files changed

Lines changed: 123 additions & 4 deletions

File tree

‎ralph/ralph_loop.sh‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,22 @@ count_fix_plan_checkboxes() {
881881
printf '%s %s %s\n' "$completed_items" "$uncompleted_items" "$total_items"
882882
}
883883

884+
# Extract the first unchecked task line from @fix_plan.md.
885+
# Returns the raw checkbox line trimmed of leading whitespace, capped at 100 chars.
886+
# Outputs empty string if no unchecked tasks exist or file is missing.
887+
# Args: $1 = path to @fix_plan.md (optional, defaults to $RALPH_DIR/@fix_plan.md)
888+
extract_next_fix_plan_task() {
889+
local fix_plan_file="${1:-$RALPH_DIR/@fix_plan.md}"
890+
[[ -f "$fix_plan_file" ]] || return 0
891+
local line
892+
line=$(grep -m 1 -E "^[[:space:]]*- \[ \]" "$fix_plan_file" 2>/dev/null || true)
893+
# Trim leading whitespace
894+
line="${line#"${line%%[![:space:]]*}"}"
895+
# Trim trailing whitespace
896+
line="${line%"${line##*[![:space:]]}"}"
897+
printf '%s' "${line:0:100}"
898+
}
899+
884900
# Collapse completed story detail lines in @fix_plan.md.
885901
# For each [x]/[X] story line, strips subsequent indented blockquote lines ( > ...).
886902
# Incomplete stories keep their detail lines intact.
@@ -1344,6 +1360,13 @@ build_loop_context() {
13441360
local total_tasks=0
13451361
read -r completed_tasks incomplete_tasks total_tasks < <(count_fix_plan_checkboxes "$RALPH_DIR/@fix_plan.md")
13461362
context+="Remaining tasks: ${incomplete_tasks}. "
1363+
1364+
# Inject the next unchecked task to give the AI a clear directive
1365+
local next_task
1366+
next_task=$(extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md")
1367+
if [[ -n "$next_task" ]]; then
1368+
context+="Next: ${next_task}. "
1369+
fi
13471370
fi
13481371

13491372
# Add circuit breaker state

‎ralph/templates/PROMPT.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
66
## Current Objectives
77
1. Review .ralph/@fix_plan.md for current priorities
88
2. Search the codebase for related code — especially which existing files need changes to integrate your work
9-
3. Implement the highest priority item using best practices
9+
3. Implement the task from the loop context (or the first unchecked item in @fix_plan.md on the first loop)
1010
4. Use parallel subagents for complex tasks (max 100 concurrent)
1111
5. Run tests after each implementation
1212
6. Update the completed story checkbox in @fix_plan.md and commit
1313
7. Read .ralph/specs/* ONLY if the task requires specific context you don't already have
1414

1515
## Key Principles
1616
- Write code within the first few minutes of each loop
17-
- ONE task per loop - focus on the most important thing
17+
- ONE task per loop — implement the task specified in the loop context
1818
- Search the codebase before assuming something isn't implemented
1919
- Creating new files is often only half the task — wire them into the existing application
2020
- Use subagents for expensive operations (file searching, analysis)
@@ -313,7 +313,7 @@ RECOMMENDATION: Blocked on [specific dependency] - need [what's needed]
313313
- examples/: Example usage and test cases
314314

315315
## Current Task
316-
Follow .ralph/@fix_plan.md and choose the most important item to implement next.
317-
Use your judgment to prioritize what will have the biggest impact on project progress.
316+
Implement the task specified in the loop context.
317+
If no task is specified (first loop), pick the first unchecked item from .ralph/@fix_plan.md.
318318

319319
Remember: Quality over speed. Build it right the first time. Know when you're done.

‎tests/bash/ralph_loop.bats‎

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,71 @@ teardown() {
588588
rm -rf "$test_dir"
589589
}
590590

591+
# ===========================================================================
592+
# extract_next_fix_plan_task
593+
# ===========================================================================
594+
595+
@test "extract_next_fix_plan_task returns first unchecked line" {
596+
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
597+
- [x] Story 1.1: Setup project scaffolding
598+
- [ ] Story 1.2: Implement user authentication
599+
- [ ] Story 2.1: Build dashboard layout
600+
EOF
601+
run extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md"
602+
603+
assert_success
604+
assert_output "- [ ] Story 1.2: Implement user authentication"
605+
}
606+
607+
@test "extract_next_fix_plan_task returns empty when all completed" {
608+
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
609+
- [x] Story 1.1: Setup project scaffolding
610+
- [X] Story 1.2: Implement user authentication
611+
EOF
612+
run extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md"
613+
614+
assert_success
615+
assert_output ""
616+
}
617+
618+
@test "extract_next_fix_plan_task returns empty when file missing" {
619+
rm -f "$RALPH_DIR/@fix_plan.md"
620+
run extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md"
621+
622+
assert_success
623+
assert_output ""
624+
}
625+
626+
@test "extract_next_fix_plan_task returns empty when file is empty" {
627+
: > "$RALPH_DIR/@fix_plan.md"
628+
run extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md"
629+
630+
assert_success
631+
assert_output ""
632+
}
633+
634+
@test "extract_next_fix_plan_task trims leading whitespace from indented items" {
635+
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
636+
- [x] Story 1.1: Setup project scaffolding
637+
- [ ] Sub-task: Configure database
638+
EOF
639+
run extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md"
640+
641+
assert_success
642+
assert_output "- [ ] Sub-task: Configure database"
643+
}
644+
645+
@test "extract_next_fix_plan_task truncates lines over 100 chars" {
646+
local long_title
647+
long_title="Story 9.1: $(printf 'x%.0s' {1..120})"
648+
echo "- [ ] ${long_title}" > "$RALPH_DIR/@fix_plan.md"
649+
run extract_next_fix_plan_task "$RALPH_DIR/@fix_plan.md"
650+
651+
assert_success
652+
local len=${#output}
653+
[[ $len -le 100 ]]
654+
}
655+
591656
# ===========================================================================
592657
# build_loop_context
593658
# ===========================================================================
@@ -609,6 +674,37 @@ EOF
609674
assert_output --partial "Remaining tasks: 2"
610675
}
611676

677+
@test "build_loop_context includes next task from fix plan" {
678+
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
679+
- [x] Story 1.1: Setup project scaffolding
680+
- [ ] Story 2.1: Build dashboard layout
681+
- [ ] Story 3.1: Add API endpoints
682+
EOF
683+
run build_loop_context 3
684+
685+
assert_success
686+
assert_output --partial "Next: - [ ] Story 2.1: Build dashboard layout."
687+
}
688+
689+
@test "build_loop_context omits next task when all completed" {
690+
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
691+
- [x] Story 1.1: Setup project scaffolding
692+
- [X] Story 2.1: Build dashboard layout
693+
EOF
694+
run build_loop_context 3
695+
696+
assert_success
697+
refute_output --partial "Next:"
698+
}
699+
700+
@test "build_loop_context omits next task when fix plan missing" {
701+
rm -f "$RALPH_DIR/@fix_plan.md"
702+
run build_loop_context 3
703+
704+
assert_success
705+
refute_output --partial "Next:"
706+
}
707+
612708
@test "build_loop_context truncates to 500 characters" {
613709
run build_loop_context 1
614710
assert_success

0 commit comments

Comments
 (0)