Re-adopt the output capture for the clean phase of run_and_clean#240
Conversation
|
Warning Review limit reached
More reviews will be available in 52 minutes and 38 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughThis change makes ChangesOutput capture readiness flow
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
run_and_clean reuses ONE ExecutionFacade across both phases, but each phase's Executor sets up and tears down its OWN output router. The layout grabbed the run-phase router at the run-phase on_ready, then on_ready's `return if @root_task_class` once-guard meant the clean phase's fresh router (built after the run router was torn down) was never adopted — the status line read the dead run-phase router for the whole clean phase, showing stale or blank output instead of live clean output. The once-guard exists to stop a nested executor from overwriting the displayed root / rebuilding the tree. But re-adopting the output capture is safe and necessary when the SAME facade re-readies (the clean phase). on_ready now refreshes @output_capture whenever the readying context is the facade that owns the display (captured as @display_facade on the first ready), and keeps root_task_class + handle_ready behind the once-guard. A nested execution runs on the current facade and router (fresh_wrapper reuses ExecutionFacade.current), so the refresh is a no-op there; an unrelated facade cannot retarget the display's capture. Verified on a PTY: the Simple status line now shows "| CLEAN removing artifact" during clean (previously the stale run line). Pinned by a component test (same-facade re-ready adopts the new router; a different facade does not) and an end-to-end test through the real executor (run_and_clean adopts two distinct routers and the clean status line reads the clean-phase output, not the dead run router). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8e0b9b8 to
021425e
Compare
The progress display is a persistent singleton (Config#build memoizes @cached_display), so it is reused across sequential top-level executions in one process. But the displayed root was never reset and @tasks accumulated, so a second top-level Task.run / run_and_clean rendered the FIRST execution's root name and an accumulated task count: [TASKI] Starting FirstRoot # run #1 [TASKI] Completed: 1/1 tasks [TASKI] Starting FirstRoot # run #2: stale root (should be SecondRoot) [TASKI] Completed: 2/2 tasks # accumulated (should be 1/1) This hits any program running two or more top-level tasks per process (scripts, test suites, REPLs). Pre-existing; surfaced by the #240 review. Fix: clear per-execution state when the OUTERMOST execution stops (on_stop at @nest_level 0), after the final frame has rendered and the render thread has been stopped by handle_stop. The next top-level execution then sees @root_task_class == nil and adopts its own root/capture via the normal on_ready path; nested executions stop at @nest_level > 0 and never trigger the reset. Resetting at stop (rather than lazily at the next on_ready) is what makes run_and_clean correct: run_and_clean calls notify_start BEFORE its first on_ready, so a reset deferred to on_ready would come too late for the start line and would mis-detect the new execution as nested. on_ready is therefore unchanged from the clean-phase-capture fix (same-facade re-adopt; otherwise ignore). reset_after_execution clears the base tallies (@tasks, @group_start_times, @start_time) plus @root_task_class / @display_facade / @output_capture, and calls the handle_reset hook; Tree re-inits its node maps, Tree::Live also drops @last_line_count (so the next frame does not erase the previous execution's final output), and Simple clears its group baselines. Pinned: end-to-end tests through the real executor for two sequential plain runs AND two sequential run_and_clean (each shows its own root and 1/1, no accumulation), a guard test that the display singleton is reused, and a test that state is cleared after stop; component tests for on_stop clearing state and a second execution adopting its own state (including readying at a raised nest level, as run_and_clean does). Existing once-guard tests updated to simulate real nesting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The progress display is a persistent singleton (Config#build memoizes @cached_display), so it is reused across sequential top-level executions in one process. But the displayed root was never reset and @tasks accumulated, so a second top-level Task.run / run_and_clean rendered the FIRST execution's root name and an accumulated task count: [TASKI] Starting FirstRoot # run #1 [TASKI] Completed: 1/1 tasks [TASKI] Starting FirstRoot # run #2: stale root (should be SecondRoot) [TASKI] Completed: 2/2 tasks # accumulated (should be 1/1) This hits any program running two or more top-level tasks per process (scripts, test suites, REPLs). Pre-existing; surfaced by the #240 review. Fix: clear per-execution state when the OUTERMOST execution stops (on_stop at @nest_level 0), after the final frame has rendered and the render thread has been stopped by handle_stop. The next top-level execution then sees @root_task_class == nil and adopts its own root/capture via the normal on_ready path; nested executions stop at @nest_level > 0 and never trigger the reset. Resetting at stop (rather than lazily at the next on_ready) is what makes run_and_clean correct: run_and_clean calls notify_start BEFORE its first on_ready, so a reset deferred to on_ready would come too late for the start line and would mis-detect the new execution as nested. on_ready is therefore unchanged from the clean-phase-capture fix (same-facade re-adopt; otherwise ignore). reset_after_execution clears the base tallies (@tasks, @group_start_times, @start_time), @root_task_class / @display_facade / @output_capture, and @spinner_index, and calls the handle_reset hook; Tree re-inits its node maps, Tree::Live also drops @last_line_count (so the next frame does not erase the previous execution's final output), and Simple clears its group baselines. The reset runs from an ensure, so a raising final render or message flush still clears state (dispatch swallows observer errors, so a leak there would silently corrupt the next run). Pinned: end-to-end tests through the real executor for two sequential plain runs AND two sequential run_and_clean (each shows its own root and 1/1, no accumulation), a guard test that the display singleton is reused, and a test that state is cleared after stop; component tests for on_stop clearing per-execution state (incl. spinner index), reset-via-ensure when the final render raises, a second execution adopting its own state (including readying at a raised nest level, as run_and_clean does), the Tree node map being rebuilt for a second execution, and Tree::Live's @last_line_count being reset. Existing once-guard tests updated to simulate real nesting. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem
Flagged during the #237–#239 review and confirmed here with instrumentation: during the clean phase of
run_and_clean, the Simple status line shows stale run-phase output (or blank) instead of live clean output.run_and_cleanreuses oneExecutionFacadeacross both phases, but each phase'sExecutor(execute/execute_clean) sets up and tears down its ownTaskOutputRouter. The layout grabs the run-phase router at the run-phaseon_ready; thenon_ready'sreturn if @root_task_classonce-guard means the clean phase's fresh router — built after the run router was torn down — is never adopted. The layout reads the dead run router for the whole clean phase.Instrumented trace (before),
ctx= facade,ctx_cap= facade's current router:Fix
The once-guard exists to stop a nested executor from overwriting the displayed root / rebuilding the tree — not to freeze the output capture. Re-adopting the capture is safe and necessary when the same facade re-readies (the clean phase).
on_readynow refreshes@output_capturewhenever the readying context is the facade that owns the display (recorded as@display_facadeon the first ready), and keepsroot_task_class+handle_readybehind the once-guard. The discriminator is facade identity, established by probing the real lifecycle:fresh_wrapperreusesExecutionFacade.current, so it runs on the current facade and router → refresh is a no-op (verified: nestedon_readyshows identicalctx/ctx_capto its parent)After:
Tests
test_layout_base.rb): same-facade re-ready adopts the new router and keeps the root; a different facade does not retarget the capture (nor the root — the once-guard).test_clean_phase_output_capture.rb, fixture tasks per CLAUDE.md):run_and_cleanthrough the real executor adopts two distinct routers, and the clean-phase status line reads the clean-phase output, not the dead run router. Deterministic across repeated runs.Suite 789 runs / 0 failures,
rake standardclean. Off master, theme-independent — merges cleanly in any order with the other open PRs.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
Bug Fixes
Tests