Repository navigation
Conversation
stop_screenrecording() refreshed the bar indicator while gpu-screen-recorder could still be alive in the SIGKILL fallback path, leaving the indicator stuck 'active' once the process was killed. A redundant --stop-recording then exited without resyncing, dead-ending every later click. Refresh after the outcome is final, and resync before exiting on a redundant stop. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Automated AI review
Verified: the force-kill ordering fix and the redundant One side effect remains on the normal (graceful) stop path. Normal stop now keeps the indicator "active" through post-processingThe single refresh moved to the end of Event order on a graceful stop, from a stubbed run of the real script with the finalize Impact: the icon is misleading, with no data loss. How long it stays wrong depends on how long post-processing takes. Timing the head's own finalize commands on synthetic 1080p60 media on a fast 16-CPU host gave about 1.3 s per recorded minute with audio (loudnorm) and about 8 s per recorded minute on the libx264 re-encode path. Slower machines, like the throttled laptop that motivated this PR, will take longer. A click during that window still recovers because of this PR's redundant-stop refresh. History: Suggested change: refresh inside each branch, so the force-kill path keeps the new ordering and the graceful path refreshes before post-processing. This variant passes the PR's test file unchanged. if pgrep -f "^gpu-screen-recorder" >/dev/null; then
pkill -9 -f "^gpu-screen-recorder"
omarchy-notification-send -u critical -t 5000 "Screen recording error" "Recording process had to be force-killed. Video may be corrupted."
toggle_screenrecording_indicator
else
toggle_screenrecording_indicator
finalize_recording
...Optional test improvement: the new tests do not exercise the graceful stop path, so the head's end-of-function placement passes them. An assertion that the refresh precedes the finalize Optional: SIGKILL delivery is asynchronous, and the new ordering test only checks that Description note: #9296 is still open and unmerged, so "the QML watchdog fixed in #9296" overstates its status. The redundant-stop resync also relies on the QML status probe finishing, because Review informationTest scope: source review of the script, the test file, the bar indicator QML and the AI process: Opus 5.5 Medium coordination and synthesis, independent Opus 5.5 Xhigh and GPT 6 Sol Xhigh technical assessments, Opus 5.5 Medium editorial check. Opt out: To stop receiving these reviews, reply to this comment saying so. |
…stop Moving the refresh to the end of stop_screenrecording fixed the force-kill path but also delayed it on the common one: a recorder that exits within the grace period kept the bar showing it as recording through the ffprobe/ffmpeg finalize, the thumbnail and the saved notification, which on a long recording or a slow machine is many seconds. Refresh in each branch instead, after the SIGKILL on one and before finalizing on the other. The test keys on ffprobe, which only finalize_recording runs, so dropping the finalize step cannot let it pass on the thumbnail's ffmpeg call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Codex Medium <noreply@openai.com>
|
Reviewed and verified. I pushed one fix to your branch, c9e86db, which changes the graceful stop path. The bug is real. I tested it on a disposable Omarchy VM with a real process named What I changed. Moving the single refresh to the end of Tests, all on the VM: Second opinion: Codex Medium reviewed it in two rounds. It agreed the bug is real and the change is proportionate; independence from my review is not guaranteed. It also found that the first version of my graceful-stop test could pass on the thumbnail's Waiting on CI and then on the maintainer. |
|
Each --stop-recording against a recorder still in uninterruptible I/O started another detached poller, and nothing retired them until the recorder exited: every click on the stale indicator added a process running pgrep once a second, and each refreshed the indicator again when it finally went. A non-blocking flock on a lock in the private runtime directory lets the first watcher run and the rest exit at once; the first still refreshes when the recorder is gone, so nothing is lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Codex Medium <noreply@openai.com>
The test released the held recorder straight after the second stop returned, so a watcher scheduled late could take the lock after the first one finished and refresh twice, and a duplicate arriving after the 200 ms window went unseen. Wait until one script process is left while the recorder is still held: with the lock that is the first watcher, without it two stay polling and the assertion fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Codex Medium <noreply@openai.com>
Ignoring its exit let a stop killed by timeout before it forked a watcher leave a count of one, which passed without the lock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Codex Medium <noreply@openai.com>
|
Reviewed your new commit, 192c626, and verified it. I pushed one fix on top of it, cf67a23, plus two commits that tighten its test. 192c626 fixes a real case. I reproduced it on a disposable Omarchy VM with a process named What I changed. Every Tests, all on the VM at 594a75a: Second opinion: Codex Medium reviewed it in four rounds. On 192c626 it found the piling-up watchers, which Greptile also flagged. In the next two rounds it found the two weaknesses in my test. It found nothing open on 594a75a. It agreed that the bounded wait plus an eventual refresh fits the problem, but independence from my review is not guaranteed. It also checked that the detached watcher cannot hang a caller that captures the script's output, because all three of its streams are redirected. Waiting on CI, then on the maintainer. |
quattro now signals the recorder through omarchy-capture-screenrecording-process and runs the toggle under a lock, but it still refreshes the indicator before the SIGKILL and exits a redundant stop without refreshing. The force-kill wait and the watcher now probe the recorder stop selected, by its saved pid, rather than pgrep, and the tests stub that helper in place of pgrep and pkill. The watcher count goes by the watch lock, since an earlier stop's preview cleanup shares the script's command line, and the late refresh is awaited by its contents rather than by the file appearing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-Authored-By: Codex Medium <noreply@openai.com>
|
This branch had stopped merging after #14431 reworked the recorder script on Why it still matters on The port. The force-kill wait and the background watcher now probe the recorder that stop selected, through the new helper, rather than Tests, on a disposable Omarchy VM:
Second opinion. Codex Medium reviewed the port in two rounds. It found nothing wrong in the script, and agreed the watcher no longer holds the toggle lock and still selects the right recorder. Its independence is not guaranteed. It also found the race in the late-refresh wait, which is fixed above. Related. I linked #9205, which this fixes. #9296 addresses the same issue with a bar-side watchdog and a 2s poll. Asked separately, Codex Medium also preferred this PR: #9296 does not touch the refresh ordering, and against This now waits on the maintainer. The repository's Tests workflow does not run on pull requests from forks, so there is no CI result here. |
Summary
Stopping a recording could leave the bar showing "Stop recording" after the recorder had exited. The stop script refreshed during the five-second SIGINT grace period's fallback decision, before sending SIGKILL. A later click with no recorder running exited without refreshing, so the stale indicator persisted.
This PR refreshes before post-processing on a graceful stop and after observing recorder exit on a force-kill. After SIGKILL, it polls for up to one second in the foreground. If the recorder remains visible, a background waiter checks once per second and refreshes when it disappears, allowing the error notification and stop command to complete. A nonblocking flock in the private runtime directory keeps one waiter even if stop is pressed repeatedly. A redundant
--stop-recordingalso refreshes before exiting 1.This addresses the script-side state synchronization problem associated with #9205. Related PR #9296 proposes periodic QML polling and a probe watchdog; it remains open and unmerged. If a recorder exits outside this stop flow, clicking the stale indicator resynchronizes it. Automatic recovery after an independent recorder exit is separate work discussed in #9296.
Validation
bash test/shell.d/screenrecording-test.shat 594a75a: 35 assertions pass, covering delayed exit after SIGKILL, exit after the foreground wait expires, force-kill notification, repeated-stop waiter deduplication, redundant-stop resync, and graceful refresh before finalizing.c9e86db, because the indicator's probe still reads recording.gpu-screen-recorderthat ignores SIGINT reaches the force-kill path and clears the actual bar indicator after exit. Active/idle screenshots were inspected for both paths. Uninterruptible I/O was simulated in the command tests, not reproduced on the live desktop.bash -nfor the changed script and test, andgit diff --check../test/clipasses at 594a75a../test/allrun at 192c626: CLI passes; 235 of 244 shell test files pass. Eight unrelated failures also occur againstc9e86db: three require the absent siblingomarchy-pkgscheckout, plus factory-reset accounts, About animation, network QR, kernel migration, and guarded pacman invocation. The ninth, locate, scans ignored Python bytecode inbin/__pycache__; copying that local cache into the baseline reproduces its UTF-8 decoding failure, and the clean baseline passes it.Fixes #9205