feat(tty): Ctrl-Z job-control suspend + clean Ctrl-C exit#8
Merged
Conversation
OpenTUI ran with `exitOnCtrlC: true`, so Ctrl-C hard-exited and bypassed dunk's ordered shutdown, and Ctrl-Z was swallowed in raw mode — dunk could not be suspended as a shell job. Add `src/core/jobControl.ts`: `installJobControlSuspendSupport` (Ctrl-Z → renderer.suspend(), SIGTSTP to the foreground group, resume on SIGCONT; restores the renderer if SIGTSTP throws; win32 no-op) and `installJobControlInterruptSupport` (Ctrl-C → ordered shutdown). Wire both into `main.tsx`, flip `exitOnCtrlC: false`, and shut down cleanly on SIGINT/SIGTERM. Lazy-imported on the app branch so cold paths keep their startup perf. Upstream's daemon `hostClient.stop()` is dropped — dunk has no daemon. Backports modem-dev/hunk#269 + #292. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Context
Backport of modem-dev/hunk#269 + #292. PR 4 of an expert-gated backport series.
OpenTUI ran with
exitOnCtrlC: true: Ctrl-C hard-exited and bypassed dunk's orderedshutdown()(renderer torn down out of order), and Ctrl-Z was swallowed in raw mode sodunkcould not be suspended as a shell job. No SIGINT/SIGTERM handling.What was changed
src/core/jobControl.ts:installJobControlSuspendSupport— Ctrl-Z →renderer.suspend(), registeronce("SIGCONT"), send SIGTSTP to the foreground process group (pid 0); resumes the renderer on SIGCONT; restores it if SIGTSTP throws;win32no-op.installJobControlInterruptSupport— Ctrl-C → dunk's orderedshutdown().main.tsx:exitOnCtrlC: false; SIGINT/SIGTERM →shutdown()(registered viaprocess.once, removed inshutdown()); both supports disposed inshutdown()(idempotent via the existingshuttingDownguard);jobControllazy-imported on the app branch so cold-path startup perf is preserved.hostClient.stop()dropped — dunk has no daemon.Expert-reviewed (terminal-lifecycle PR): lifecycle/race ordering,
pid 0target, and thehostClient.stop()drop validated. Incorporated: confirmedCliRenderer.suspend()/resume()flush synchronously (native restore before SIGTSTP), confirmed SIGTERM still exits (shutdownSession→process.exit(0)), added the "ignore Ctrl-Z after renderer destroyed" unit test.Verification
bun run typecheck,bun run lint: clean.jobControl.test.ts: 10 pass — both functions, dispose cleanup, SIGTSTP-throw restore, win32 no-op, ignore-after-destroy for Ctrl-C and Ctrl-Z.src/core: 179 pass. PTY suite: 21 pass (real launches unaffected byexitOnCtrlC: false).exit 0with terminal-restore sequences emitted (alt-screen exit, cursor/mouse restore) — the #292 path verified end-to-end on a real TTY.Out of scope
The interactive Ctrl-Z → shell
fg→ resume cycle requires a controlling-TTY foreground process group and can't be faithfully reproduced in CI (a synthetic self-SIGTSTP outside a foreground job doesn't stop under Bun — an orphaned-process-group artifact, not a defect). The dunk-owned logic is unit-tested; the OS path is manual-smoke, matching upstream which also unit-testsjobControlonly.🤖 Generated with Claude Code