Auto-refresh TUI task list after task completion#361
Auto-refresh TUI task list after task completion#361mwarger wants to merge 6 commits intosubsy:mainfrom
Conversation
|
@mwarger is attempting to deploy a commit to the plgeek Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughThe PR implements automatic task list refresh in the TUI after task completion. It adds post-completion tracker refresh to the engine (with debouncing in parallel mode), propagates refreshed tasks through component props, and enables the UI to recompute and display updated tasks. Changes
Sequence DiagramsequenceDiagram
participant Engine
participant Tracker
participant RunApp as TUI/RunApp
participant UI as UI Render
Engine->>Tracker: Task completed, call refreshTasks()
activate Tracker
Tracker->>Tracker: Fetch tasks (status: open|in_progress|completed)
Tracker->>Engine: Emit tasks:refreshed event with TrackerTask[]
deactivate Tracker
Engine->>RunApp: Update parallelRefreshedTasks prop
activate RunApp
RunApp->>RunApp: useEffect triggers on refreshedTasks change
RunApp->>RunApp: convertTasksWithDependencyStatus()
RunApp->>UI: Re-render with updated task list
deactivate RunApp
UI->>UI: Display new beads in task list
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #361 +/- ##
==========================================
- Coverage 47.35% 47.31% -0.04%
==========================================
Files 111 111
Lines 36446 36492 +46
==========================================
+ Hits 17258 17267 +9
- Misses 19188 19225 +37
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/commands/run.tsx (1)
2197-2211: ClearrefreshTimerduring cleanup to avoid unnecessary work after shutdown.The timer is not cleared when the TUI shuts down (see cleanup effect at lines 2617-2620). While the code is safe due to the
triggerRerender?.()null-check, the async callback could still execute after cleanup begins, querying the tracker unnecessarily.♻️ Suggested fix
Add timer cleanup in the
useEffectcleanup function around line 2617:return () => { triggerRerender = null; clearInterval(pollInterval); + if (refreshTimer) { + clearTimeout(refreshTimer); + refreshTimer = null; + } };Note: Since
refreshTimeris declared outside the component, you may need to adjust the scope or use a ref pattern if you want the cleanup to access it reliably.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/run.tsx` around lines 2197 - 2211, The debounced tracker refresh timer (refreshTimer used by scheduleTrackerRefresh) isn't cleared during component cleanup, so add cleanup logic to clearTimeout(refreshTimer) and set refreshTimer = null to prevent the async callback from running after shutdown; if refreshTimer's scope prevents access from the useEffect cleanup, refactor refreshTimer into a React ref (e.g., useRef) or make it component-scoped so the cleanup in the effect that currently runs at lines ~2617–2620 can call clearTimeout and null out the ref, and keep existing checks (tracker/triggerRerender) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/hooks/dcg.json:
- Around line 7-8: The hook config currently hard-codes a user-specific binary
path for the "bash" and "powershell" entries, which will break on other
machines/CI; update the .github/hooks/dcg.json entries for "bash" and
"powershell" to use a portable command (e.g., just "dcg",
"./node_modules/.bin/dcg", or an env-driven path) so the hook resolves in
different environments; ensure the chosen portable value is consistent for both
the "bash" and "powershell" keys and documented in the repo readme or
contributing guide.
In `@src/engine/index.ts`:
- Around line 1338-1342: The call to await this.refreshTasks() inside the block
guarded by taskCompleted && !this.forcedTask can throw and currently bubbles up,
causing a completed iteration to be marked failed; make this refresh best-effort
by wrapping the call in a try/catch around refreshTasks() (inside the same
conditional), swallow the error (or log it via the existing
logger/processLogger) and do not rethrow so the iteration remains successful
even if refreshTasks() fails; update the block referencing taskCompleted,
forcedTask, and refreshTasks accordingly.
In `@src/tui/components/RunApp.tsx`:
- Around line 575-579: The effect currently ignores empty refresh results
because it only updates state when parallelRefreshedTasks.length > 0; change the
condition in the useEffect so that it applies converted tasks even when the
array is empty by checking for null/undefined instead (e.g., if
parallelRefreshedTasks != null) and then calling
setTasks(convertTasksWithDependencyStatus(parallelRefreshedTasks)); this
involves editing the useEffect that references parallelRefreshedTasks, setTasks,
and convertTasksWithDependencyStatus to remove the length>0 guard but still
avoid running on null/undefined.
- Around line 2360-2362: handleKeyboard currently calls onRefreshTasks but the
useCallback dependency array for handleKeyboard doesn't include onRefreshTasks;
update the dependency array of the useCallback that defines handleKeyboard to
include onRefreshTasks so the callback always references the latest prop (keep
the existing optional check and invocation logic intact).
---
Nitpick comments:
In `@src/commands/run.tsx`:
- Around line 2197-2211: The debounced tracker refresh timer (refreshTimer used
by scheduleTrackerRefresh) isn't cleared during component cleanup, so add
cleanup logic to clearTimeout(refreshTimer) and set refreshTimer = null to
prevent the async callback from running after shutdown; if refreshTimer's scope
prevents access from the useEffect cleanup, refactor refreshTimer into a React
ref (e.g., useRef) or make it component-scoped so the cleanup in the effect that
currently runs at lines ~2617–2620 can call clearTimeout and null out the ref,
and keep existing checks (tracker/triggerRerender) intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a695a9b1-1283-4baa-86da-7d0e03025d4c
📒 Files selected for processing (6)
.beads/issues.jsonl.github/hooks/dcg.jsonsrc/commands/run.tsxsrc/engine/index.test.tssrc/engine/index.tssrc/tui/components/RunApp.tsx
.github/hooks/dcg.json contained hardcoded local paths and was not intended to be checked in. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Wrap refreshTasks() in try/catch so refresh failures don't mark completed iterations as failed - Accept empty refresh results to avoid showing stale tasks - Add onRefreshTasks to handleKeyboard dependency array to fix stale closure - Clear refreshTimer on cleanup to prevent post-shutdown timer fires Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Closes #360
Summary
rworker:completedandmerge:completedeventsrkey to work in parallel mode (previously requiredenginewhich doesn't exist in parallel mode)Details
Single mode (
src/engine/index.ts): After a task completes (and after auto-commit), callthis.refreshTasks()which re-queries the tracker and emits the existingtasks:refreshedevent. Skipped in worker mode (forcedTask) since workers don't own the TUI.Parallel mode (
src/commands/run.tsx): Pass the tracker intorunParallelWithTuiand add a debounced (2s)scheduleTrackerRefresh()that queriestracker.getTasks()and stores the result inparallelState.refreshedTasks. Called onworker:completedandmerge:completed.TUI (
src/tui/components/RunApp.tsx): AddparallelRefreshedTasksprop with auseEffectthat updates the task list when it changes. AddonRefreshTasksprop so therkey works in parallel mode.Tests (
src/engine/index.test.ts): 3 new tests forrefreshTasks()— event emission,totalTaskscount accuracy, and no-op when tracker is null.What this does NOT do
.beads/beads.dbtasks:refreshedpipeline)Reproducing locally
To test this yourself, create test beads that simulate an agent creating new beads during execution:
Then run ralph-tui against the
ralph-tui-sqt5epic. When the agent executesralph-tui-sqt5.1, it should create a new bead viabr create. Before this PR, the new bead won't appear in the TUI until you pressr. After this PR, it appears automatically.To clean up afterward:
Test plan
br create— new bead should appear in TUI without pressingrrin both single and parallel mode to confirm it still worksbun run typecheck && bun run buildpassesbun test src/engine/index.test.ts— 22 tests pass (3 new)🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Tests