feat(progress): add clear_jobs() to remove all top-level jobs#94
Conversation
`stop()` and `stop_clear()` end the rendering session but leave completed jobs registered in the global JOBS list (default ProgressJobDoneBehavior is Keep). When a caller starts a fresh session after stop, the next refresh re-renders every job still registered alongside the new ones. Add `clear_jobs()` so callers can explicitly drop those jobs after stopping. This is additive — `stop()`'s contract is unchanged, and `job_count()`/`active_jobs()` continue to reflect what's currently registered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a clear_jobs function to the progress state to allow clearing the job registry, along with a corresponding unit test. Review feedback highlights that the current implementation is incomplete for its intended purpose of starting a fresh session; it fails to reset the STOPPING flag and other internal states like LAST_OUTPUT, which would prevent the background rendering thread from restarting for new jobs.
| pub fn clear_jobs() { | ||
| JOBS.lock().unwrap().clear(); | ||
| } |
There was a problem hiding this comment.
The current implementation of clear_jobs() only clears the JOBS registry but does not reset the STOPPING flag. Since stop() and stop_clear() set STOPPING to true, any subsequent attempt to start a new progress session (by adding a new job) will fail to trigger the background rendering thread because notify() and start() return early when STOPPING is true.
To support the "fresh session" use case described in the PR, clear_jobs() should reset STOPPING to false. Additionally, clearing LAST_OUTPUT and calling clear_osc_progress() ensures a completely clean state for the next session, preventing the smart-refresh optimization from potentially skipping the first frame of the new session.
| pub fn clear_jobs() { | |
| JOBS.lock().unwrap().clear(); | |
| } | |
| pub fn clear_jobs() { | |
| JOBS.lock().unwrap().clear(); | |
| STOPPING.store(false, Ordering::Relaxed); | |
| LAST_OUTPUT.lock().unwrap().clear(); | |
| clear_osc_progress(); | |
| } |
Greptile SummaryAdds a new public
Confidence Score: 5/5Safe to merge; purely additive API with no changes to existing stop/start semantics. The change is a small, additive function with no modifications to existing behavior. The one noteworthy issue — that the background thread may still be sleeping when STOPPING is reset, leaving a stale thread alongside a newly spawned one — is a narrow race masked in practice by REFRESH_LOCK serialization and LAST_OUTPUT smart-refresh, and self-corrects on the next stop() call. src/progress/state.rs — specifically the interaction between clear_jobs() and the background thread's notify_wait() sleep cycle. Important Files Changed
Reviews (2): Last reviewed commit: "fix(progress): reset STOPPING in clear_j..." | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 396c2a5. Configure here.
`stop()` and `stop_clear()` set STOPPING=true and nothing ever clears it. Without resetting it, the documented "fresh session" workflow silently half-breaks: notify() and start() short-circuit on STOPPING so no background thread restarts, and update() returns early so animated spinners and incremental messages don't render. Only synchronous terminal-state flushes from set_status(Done/Failed/...) still get through via refresh_once (which doesn't check STOPPING) — which is why the symptom in mise (jdx/mise#9774) was a final-frame re-render rather than complete silence. Have clear_jobs also reset STOPPING so a caller who explicitly opts into a new session actually gets one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ng jobs clx 2.1.0 (jdx/clx#94) ships `clear_jobs()`, which removes all top-level jobs from clx's global JOBS registry and resets the STOPPING flag. That makes the mise-side `tracked_jobs` Vec — and the `pub(crate)` widening of `ProgressReport.job` it required — unnecessary. reset_jobs() now drops `header_job` and calls `progress::clear_jobs()`. Behavior is unchanged for the bug this PR fixes (#9774), and the "start a fresh session after stop" workflow now works fully (animated spinners and incremental updates render again, not just the final terminal-state flush via refresh_once). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Summary
stop()andstop_clear()end the rendering session — they stop the background thread, render/clear the display, and resetLINES— but they leave the completed jobs registered in the globalJOBSVec. With the defaultProgressJobDoneBehavior::Keep, a caller that starts a fresh session by adding a new job afterstopwill see the next refresh re-render every previously-completed job alongside the new ones.This bit mise in jdx/mise#9774:
mise upgradefinishes installing (callsstop()), then adds an "uninstall" progress job, and the installed-tools block gets printed twice.Adding
clear_jobs()lets callers explicitly drop the registered jobs after stopping, without needing to holdArc<ProgressJob>references to each.What's not changing
stop()/stop_clear()semantics — narrow contract preservedjob_count()/active_jobs()— continue to reflect what's currently registered, including completed-but-still-displayed jobsclear_jobs()is purely additive. mise (and any other caller that wants the old behavior) can opt in.Test plan
cargo buildcargo test -- --test-threads=1(117 passing, including newtest_clear_jobs)cargo clippy(no new warnings)cargo fmt --checkFollow-up: jdx/mise#9779 currently has a mise-side workaround tracking jobs in a Vec; once this lands and clx is released, the mise PR will be simplified to call
progress::clear_jobs()instead.🤖 Generated with Claude Code
Note
Medium Risk
Modifies global progress lifecycle state by clearing
JOBSand resetting theSTOPPINGlatch, which could affect rendering/thread behavior across sessions if misused.Overview
Adds a new public
clear_jobs()API to explicitly drop all registered top-level progress jobs and reset the internal stopped state, allowing a new progress “session” to render/animate normally afterstop()/stop_clear().Includes new unit tests covering registry clearing behavior and verifying that
clear_jobs()resets theSTOPPINGflag so subsequent jobs refresh correctly.Reviewed by Cursor Bugbot for commit 0bec5e3. Bugbot is set up for automated code reviews on this repo. Configure here.