Reset the progress display between top-level executions#242
Conversation
WalkthroughRefines ChangesPer-execution display state reset
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 docstrings
🧪 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 |
194a1d5 to
6d15bec
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), @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 <[email protected]>
6d15bec to
34b060b
Compare
Problem
Surfaced by the #240 adversarial review. The progress display is a persistent singleton (
Config#buildmemoizes@cached_display), reused across sequential top-level executions in one process. The displayed root was never reset and@tasksaccumulated — so a second top-levelTask.run/run_and_cleanrendered the first execution's root name and an accumulated task count:Pre-existing; hits any program running ≥2 top-level tasks per process — scripts, test suites, REPLs.
Fix
Clear per-execution state when the outermost execution stops (
on_stopat@nest_level == 0), after the final frame has rendered andhandle_stophas stopped the render thread. The next top-level execution then sees@root_task_class == niland adopts its own root/capture via the normalon_readypath; nested executions stop at@nest_level > 0and never trigger the reset.Why reset at stop, not lazily at the next
on_ready:run_and_cleancallsnotify_startbefore its firston_ready. A reset deferred toon_readywould come too late for the start line and mis-detect the new execution as nested. Resetting at stop sidesteps the ordering and leaveson_readyunchanged from #240. (An earlier draft keyed the reset off@nest_levelinon_readyand was caught during self-review failing exactly therun_and_clean×2 case — hence the stop-based approach, pinned by a dedicated test.)reset_after_execution(run from anensure, so a raising final render still clears state —dispatchswallows observer errors) clears the base tallies (@tasks,@group_start_times,@start_time),@root_task_class/@display_facade/@output_capture, and@spinner_index, then calls ahandle_resethook; Tree re-inits its node maps, Tree::Live also drops@last_line_count, and Simple clears its group baselines.Tests
run_and_clean(each shows its own root and1/1, no accumulation); singleton-reused guard; state-cleared-after-stop.on_stopclears per-execution state incl. spinner index; reset-via-ensurewhen the final render raises; a second execution adopts its own state (incl. readying at a raised nest level); the Tree node map is rebuilt for a second execution; Tree::Live's@last_line_countis reset.Suite 856 runs / 0 failures,
rake standardclean. Off master, independent.Adversarial review
A fresh 15-agent review of the reset-on-stop design (the earlier review of a discarded
@nest_leveldraft was stopped). 4 confirmed findings, all folded: spinner index not reset (LOW); reset skipped if the final render raises → moved into anensure(MED); two coverage gaps (no two-run Tree-node test, no Tree::Live@last_line_counttest) → tests added and mutation-verified. Refuted: post-runtask_statereturning nil is an intentional, accepted semantics change;@contextretaining a stale facade is informational; nested/clean unaffected.🤖 Generated with Claude Code