feat: warn and block a second Ctrl-C during eval cleanup#1972
Merged
Conversation
…ting cleanup The first Ctrl-C/SIGTERM now warns that cleanup is running and raises KeyboardInterrupt so asyncio unwinds each rollout's `finally` (container/sandbox teardown). Further shutdown signals during that window are swallowed so an impatient second Ctrl-C can't cut cleanup short and orphan those resources. Works in both modes: non-rich echoes the notice to stderr; rich renders it as a banner in the dashboard, whose refresh loop now keeps painting through teardown. The CLI exits on the conventional 130 without a traceback. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ApprovabilityVerdict: Needs human review This PR modifies signal handling to swallow subsequent Ctrl-C presses during cleanup, preventing forced exit behavior. While the intent is defensive (preventing orphaned containers), changing interrupt handling semantics is a significant runtime behavior change that warrants human review. You can customize Macroscope's approvability policy. Learn more. |
…dashboard Move the graceful-shutdown notice from a banner atop the dashboard to a footer under the rollouts, beside the --push line, styled yellow and all-lowercase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use the standard library logger (intercepted into loguru) instead of a raw stderr write, so the notice is timestamped and recorded in eval.log in both modes. In rich mode it lands in the log file only (the dashboard footer is the on-screen echo), so the `rich` param is no longer needed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
samsja
approved these changes
Jul 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SIGINT) orSIGTERMduringeval, warn that cleanup is in progress and raiseKeyboardInterruptso asyncio unwinds each rollout'sfinally— the path that tears down containers/sandboxes and any worker pool the run spawned.SIGKILL.--pushline; console logging is silenced in rich mode), and the live refresh loop keeps repainting through teardown instead of freezing on the last frame.130code without dumping aKeyboardInterrupttraceback; partial results remain on disk (traces are persisted incrementally).verifiers/v1/utils/interrupt.pyhandler coveringSIGINT+SIGTERM.Related
Overlaps with #1925 (open, follow-up to the merged #1920). That PR surfaces the teardown drain from the runtime layer (
Runtime.stopaggregate warning +run_shielded) and explicitly leaves the--richdashboard surfacing as future work — which this PR implements.Note the deliberate divergence on the second Ctrl-C: #1925 makes a second press abort immediately ("never subordinate a user abort"), whereas this PR blocks it so cleanup always runs to completion. These two policies are mutually exclusive and should be reconciled before both land.
Verification
Signal handler and rendering (unit-level):
KeyboardInterrupt,cleaning_up()flips true, second signal is swallowed with a "ctrl-c ignored" notice (rich mode: no stderr spam, footer shows instead)._interrupt_footer()is hidden before Ctrl-C and renders below the rollouts (yellow, lowercase) after.live_viewkeeps refreshing during a shielded teardown (simulating asyncio's cancel-all-tasks on Ctrl-C) and terminates cleanly without hanging.End-to-end (real
evalCLI, rollout hung on an unresponsive endpoint so teardown is in-flight):SIGINT→interrupted — cleaning up, please wait..., teardown runs to completion (interception down).SIGINTmid-teardown does not abort — the process finishes cleanup and exits130(verified the handler runs on the 2nd/3rd signal and teardown completes uninterrupted).Breaking
None.
--serverand legacy paths are unaffected (both run non-rich);server + richremains rejected at config validation.🤖 Generated with Claude Code
Note
Medium Risk
Changes process-wide signal handling and shutdown semantics (second Ctrl-C no longer kills immediately), which affects container/sandbox teardown reliability but is limited to the eval CLI paths that call install_interrupt.
Overview
Introduces
verifiers/v1/utils/interrupt.pyso the eval CLI handles SIGINT and SIGTERM in one place: the first signal logs a warning, sets a globalcleaning_upflag, and raisesKeyboardInterruptso asyncio can run rollout teardown; later signals during cleanup are ignored (with a “ctrl-c ignored” warning in non-rich mode).The eval entrypoint calls
install_interrupt()instead of remapping only SIGTERM, catchesKeyboardInterrupt, and exits with code 130 without a traceback. Partial traces on disk are unchanged.In rich mode,
_interrupt_footer()shows a yellow cleanup line under the rollouts (alongside--pushstatus), andlive_viewkeeps repainting whilecleaning_up()is true even when Ctrl-C cancels the refresh task—so the dashboard does not freeze mid-teardown.Reviewed by Cursor Bugbot for commit 7e8df73. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Warn and block a second Ctrl-C during eval cleanup
install_interrupt()andcleaning_up()helpers; the first SIGINT/SIGTERM sets a cleanup flag and raisesKeyboardInterrupt, subsequent signals during cleanup are silently ignored.install_interrupt()and catchesKeyboardInterruptto exit with code 130.Macroscope summarized 7e8df73.