fix(tools): interpret lethal signal exits in background-process notifications - #88379
Open
pierrenode wants to merge 1 commit into
Open
fix(tools): interpret lethal signal exits in background-process notifications#88379pierrenode wants to merge 1 commit into
pierrenode wants to merge 1 commit into
Conversation
…ications 204302b added terminal_tool._interpret_signal_exit() (11 curated signals: SIGKILL/SIGSEGV/SIGBUS/SIGFPE/etc.) and wired it into the foreground terminal_tool exit-code report via _interpret_exit_code(). It never touched format_process_notification() -- the shared formatter for background/detached process completions, called from process_registry.py itself, tui_gateway/server.py (x2), and gateway/run.py's async-delegation path. That formatter kept its own, much older check that recognizes only SIGTERM: if _exit in {-15, 143, "-15", "143"}: _signal = ", SIGTERM" A background process killed by the OOM killer (exit 137/-9 -- "the big one" per 204302b's own commit message) or crashing with SIGSEGV (139/-11) reached the model as a bare "(exit code 137)" with zero explanation, exactly the failure mode 204302b set out to fix, just on a different surface. Fix: format_process_notification() now resolves exit_code (int or the numeric-string shape some producers use) through terminal_tool's _interpret_signal_exit() via a lazy import (existing established pattern in this pair of modules -- process_registry.py already lazy-imports terminal_tool._rewrite_compound_background for the same reason: a circular top-level import). The note is surfaced as its own line rather than crammed into the exit-code parenthetical, matching how the foreground path surfaces it as its own `exit_code_meaning` field rather than a compact suffix. No test previously asserted on the exact SIGTERM-suffix string, so the format change (moving the note out of the parenthetical) does not touch any covered contract; grepped tests/tools/test_watch_patterns.py, test_async_delegation.py, and test_context_compressor_zero_user_ provenance.py's format_process_notification usages -- all exercise other branches (watch_overflow, async_delegation, or only the "[IMPORTANT: Background process" prefix), none reach this code path. Mutation-verified: reverting the fix makes 3 of the 7 new tests fail against pre-fix code (SIGKILL as int, SIGKILL via the 128+signum shell convention, SIGSEGV as a numeric string); the other 4 (SIGTERM still recognized, clean exit, ordinary non-zero exit, unknown "?" placeholder) correctly pass either way since they were never exposed to the bug. Broader neighbor suite (160 tests across test_process_registry.py, test_watch_patterns.py, test_async_delegation.py, test_context_compressor_zero_user_provenance.py, test_terminal_tool.py) and the dedicated test_terminal_signal_exit.py (27 tests) pass unchanged. ruff clean.
Contributor
Right unification: background-process notifications kept a stale SIGTERM-only check while the foreground path had gained the full
No blocking issues found beyond item 1's tiny parse gap. |
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
204302b added
terminal_tool._interpret_signal_exit()(11 curated signals: SIGKILL/SIGSEGV/SIGBUS/SIGFPE/etc.) and wired it into the foreground terminal_tool exit-code report via_interpret_exit_code(). It never touchedformat_process_notification()— the shared formatter for background/detached process completions, called fromprocess_registry.pyitself,tui_gateway/server.py(x2), andgateway/run.py's async-delegation path. That formatter kept its own, much older check that recognizes only SIGTERM:A background process killed by the OOM killer (exit 137/-9 — "the big one" per 204302b's own commit message) or crashing with SIGSEGV (139/-11) reached the model as a bare
(exit code 137)with zero explanation — exactly the failure mode 204302b set out to fix, just on a different surface.Changes
tools/process_registry.py: new_background_exit_signal_note()resolvesexit_code(int or the numeric-string shape some producers use) throughterminal_tool._interpret_signal_exit()via a lazy import — mirroring the existing established pattern in this pair of modules (process_registry.pyalready lazy-importsterminal_tool._rewrite_compound_backgroundfor the same reason: a circular top-level import). The note is surfaced as its own line rather than crammed into the exit-code parenthetical, matching how the foreground path surfaces it as its ownexit_code_meaningfield rather than a compact suffix.tests/tools/test_process_registry.py: 7 new tests (TestBackgroundExitSignalNote).No test previously asserted on the exact SIGTERM-suffix string, so the format change (moving the note out of the parenthetical) does not touch any covered contract — grepped
test_watch_patterns.py,test_async_delegation.py, andtest_context_compressor_zero_user_provenance.py'sformat_process_notificationusages: all exercise other branches (watch_overflow, async_delegation, or only the"[IMPORTANT: Background process"prefix), none reach this code path.Test plan
"?"placeholder) correctly pass either way since they were never exposed to the bugtest_process_registry.py,test_watch_patterns.py,test_async_delegation.py,test_context_compressor_zero_user_provenance.py,test_terminal_tool.py) and the dedicatedtest_terminal_signal_exit.py(27 tests) pass unchangedruff checkclean on both changed filesCompeting PR check
format_process_notification's neighborhood (redaction forwatch_match/watch_disabled/watch_overflow_*) but ingateway/run.py's separate_format_gateway_process_notification— confirmed that function has no completion/exit-code branch of its own (evt_type == "completion"falls through toreturn None; the actual exit-code text is generated exclusively byprocess_registry.py::format_process_notification, which all 5 call sites — including the 2 intui_gateway/server.py— delegate to directly). No overlap.tools/process_registry.pyandtests/tools/test_process_registry.py, but its diff addsremove()/clear_finished()registry methods and extends theprocesstool's action schema/dispatch — it never touchesformat_process_notification()'s body. No overlap.