docs: add worklog for ralph-wiggum iteration counter fix
This commit is contained in:
parent
bffa966e76
commit
9efc9aaa9f
File diff suppressed because one or more lines are too long
147
docs/worklogs/2026-01-23-ralph-wiggum-iteration-counter-fix.md
Normal file
147
docs/worklogs/2026-01-23-ralph-wiggum-iteration-counter-fix.md
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
---
|
||||||
|
title: "Fix ralph-wiggum iteration counter stuck at 1"
|
||||||
|
date: 2026-01-23
|
||||||
|
keywords: [ralph-wiggum, pi-extension, iteration-counter, bug-fix, code-review]
|
||||||
|
commits: 0
|
||||||
|
compression_status: uncompressed
|
||||||
|
---
|
||||||
|
|
||||||
|
# Session Summary
|
||||||
|
|
||||||
|
**Date:** 2026-01-23
|
||||||
|
**Focus Area:** Fixing ralph-wiggum extension iteration counter bug
|
||||||
|
|
||||||
|
# Accomplishments
|
||||||
|
|
||||||
|
- [x] Diagnosed root cause of iteration counter staying stuck at 1
|
||||||
|
- [x] Fixed `ralph_done` tool to increment iteration before `hasPendingMessages()` check
|
||||||
|
- [x] Applied code review findings (4 issues fixed)
|
||||||
|
- [x] Closed issue skills-s984
|
||||||
|
|
||||||
|
# Key Decisions
|
||||||
|
|
||||||
|
## Decision 1: Always increment iteration, defer prompt delivery
|
||||||
|
|
||||||
|
- **Context:** The `hasPendingMessages()` guard was blocking iteration increment entirely, causing counter to stay at 1
|
||||||
|
- **Options considered:**
|
||||||
|
1. Remove the guard entirely - Risk of duplicate prompts
|
||||||
|
2. Increment first, only skip prompt delivery if pending - Counter accurate, prompts still controlled
|
||||||
|
3. Check for pending USER messages only - Would require API change
|
||||||
|
4. Use re-entry flag - More complex, doesn't solve root cause
|
||||||
|
- **Rationale:** Option 2 keeps counter accurate while still preventing duplicate prompt delivery
|
||||||
|
- **Impact:** Iteration counter now reflects actual progress; UI stays accurate; reflection checkpoints work
|
||||||
|
|
||||||
|
## Decision 2: Move reflection calculation before pending check
|
||||||
|
|
||||||
|
- **Context:** If reflection was due but pending messages blocked, reflection was skipped entirely (not deferred)
|
||||||
|
- **Rationale:** Reflection state should be recorded regardless of prompt delivery
|
||||||
|
- **Impact:** `lastReflectionAt` is now set even when prompt is deferred; message indicates "(reflection pending)"
|
||||||
|
|
||||||
|
# Problems & Solutions
|
||||||
|
|
||||||
|
| Problem | Solution | Learning |
|
||||||
|
|---------|----------|----------|
|
||||||
|
| `hasPendingMessages()` always true during normal operation | Move increment before the check, save state in both branches | Guards can block more than intended; order matters |
|
||||||
|
| Reflection tracking skipped when prompt deferred | Calculate `needsReflection` before pending check, update `lastReflectionAt` in both paths | Side effects should happen before conditional returns |
|
||||||
|
| Error message "not active" was ambiguous | Split into two checks: null state vs wrong status, with specific messages | Better error messages help debugging |
|
||||||
|
| Magic expression `state.iteration - 1` repeated | Extract to `const completedIteration` | DRY applies to expressions too |
|
||||||
|
|
||||||
|
# Technical Details
|
||||||
|
|
||||||
|
## Code Changes
|
||||||
|
|
||||||
|
- Total files modified: 1
|
||||||
|
- Key files changed:
|
||||||
|
- `~/.pi/agent/extensions/ralph-wiggum/index.ts` - Fixed `ralph_done` tool execute function
|
||||||
|
|
||||||
|
## Before (broken)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
if (ctx.hasPendingMessages()) {
|
||||||
|
return { content: [{ type: "text", text: "Pending messages already queued. Skipping ralph_done." }], details: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment iteration - NEVER REACHED when pending messages exist
|
||||||
|
state.iteration++;
|
||||||
|
```
|
||||||
|
|
||||||
|
## After (fixed)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Always increment iteration to keep counter accurate
|
||||||
|
state.iteration++;
|
||||||
|
const completedIteration = state.iteration - 1;
|
||||||
|
|
||||||
|
// Check max iterations before anything else
|
||||||
|
if (state.maxIterations > 0 && state.iteration > state.maxIterations) {
|
||||||
|
completeLoop(ctx, state, ...);
|
||||||
|
return { ... };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate reflection status before pending check (so we don't skip it)
|
||||||
|
const needsReflection = state.reflectEvery > 0 && completedIteration % state.reflectEvery === 0;
|
||||||
|
if (needsReflection) state.lastReflectionAt = state.iteration;
|
||||||
|
|
||||||
|
// If there are pending messages, record progress but skip prompt delivery
|
||||||
|
if (ctx.hasPendingMessages()) {
|
||||||
|
saveState(ctx, state);
|
||||||
|
updateUI(ctx);
|
||||||
|
const reflectNote = needsReflection ? " (reflection pending)" : "";
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Iteration ${completedIteration} complete.${reflectNote} Prompt deferred - pending messages.` }],
|
||||||
|
details: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Review Findings Fixed
|
||||||
|
|
||||||
|
1. **[ERROR] MED** - Split null/inactive state checks with specific error messages
|
||||||
|
2. **[SMELL] LOW** - Extracted `completedIteration` constant
|
||||||
|
3. **[SMELL] LOW** - Standardized message format ("complete" in both paths)
|
||||||
|
4. **[STRUCTURAL]** - Moved reflection calculation before pending-messages check
|
||||||
|
|
||||||
|
# Process and Workflow
|
||||||
|
|
||||||
|
## What Worked Well
|
||||||
|
|
||||||
|
- Issue had detailed bug report with root cause analysis already done (docs/work/2026-01-22-ralph-iteration-counter-bug.md)
|
||||||
|
- Code review lenses caught additional issues beyond the primary fix
|
||||||
|
- Fixing all issues in one pass
|
||||||
|
|
||||||
|
## What Was Challenging
|
||||||
|
|
||||||
|
- Extension not in git repo (deployed separately), couldn't easily diff changes
|
||||||
|
- No TypeScript compiler readily available to validate syntax
|
||||||
|
|
||||||
|
# Learning and Insights
|
||||||
|
|
||||||
|
## Technical Insights
|
||||||
|
|
||||||
|
- `ctx.hasPendingMessages()` returns true whenever tool responses are queued - essentially always during multi-tool agent operation
|
||||||
|
- Guards that block early can prevent important state updates from happening
|
||||||
|
- The order of operations matters: increment → check limits → calculate side effects → check guards → deliver
|
||||||
|
|
||||||
|
## Architectural Insights
|
||||||
|
|
||||||
|
- The "always save state, optionally defer prompt" pattern is cleaner than "guard blocks everything"
|
||||||
|
- Error messages should distinguish between different failure modes
|
||||||
|
|
||||||
|
# Context for Future Work
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- Should there be a mechanism to force prompt delivery even with pending messages?
|
||||||
|
- Should deferred reflections trigger on the next non-deferred iteration?
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- Test the fix in a real ralph loop session
|
||||||
|
- Consider adding this pattern to pi extension documentation
|
||||||
|
|
||||||
|
# Session Metrics
|
||||||
|
|
||||||
|
- Commits made: 0 (not yet committed)
|
||||||
|
- Files touched: 1
|
||||||
|
- Lines added/removed: ~30/~20
|
||||||
|
- Issues closed: 1 (skills-s984)
|
||||||
Loading…
Reference in a new issue