Skip to content

fix(tui): tear the sandbox down when Ctrl+Q quits twice - #1334

Open
poison-control wants to merge 1 commit into
usestrix:mainfrom
poison-control:fix/sandbox-leak-on-quit
Open

poison-control wants to merge 1 commit into
usestrix:mainfrom
poison-control:fix/sandbox-leak-on-quit

Conversation

@poison-control

@poison-control poison-control commented Sep 18, 2026

Copy link
Copy Markdown

Problem

Fixes #1079: Ctrl+Q leaves the scan's container running, so repeated sessions pile up orphans.

Ctrl+Q quits twice. The sidecar sends the quit command and exits in the same batch (strix/interface/tui/internal/app/update.go:20):

return m, tea.Batch(send(m.client, "app.quit", map[string]any{}), tea.Quit)

Both halves land on GoTuiRuntime.quit():

  1. The command arrives on the backend socket. controller._quit calls quit(), which cancels scan_task. run_strix_scan's finally begins teardown and suspends on the docker delete inside session_manager.cleanup().
  2. The sidecar has already exited, so wait_process returns and run()'s finally calls quit() again. scan_task is still tearing down, so if not scan_task.done(): scan_task.cancel() fires a second time.

That second cancel lands on the delete the teardown is awaiting. cleanup() never finishes and the container stays up.

4c1f00d fixed the same cancellation-path leak during staging. This one sits one step later, at teardown.

Fix

GoTuiRuntime.quit() becomes idempotent. The first caller owns the cancel and the wait as its own task, and later callers await that task instead of cancelling again. The await is shielded, since awaiting a task hands it the waiter's own cancellation, which is the thing being guarded against.

Testing

test_quitting_twice_lets_the_sandbox_teardown_finish (tests/test_go_tui_runtime.py) drives the real GoTuiRuntime.quit() through the Ctrl+Q sequence: quit while the scan runs, then quit again while the teardown waits on the daemon. The teardown is held open on an event rather than a sleep, so the test does not depend on timing.

Before:

E       AssertionError: the sandbox container was left running
E       assert [] == ['test-run']

After:

1 passed, 34 deselected in 0.53s

Checks:

uv run pytest tests/test_go_tui_runtime.py -q   # 35 passed
uv run pytest -q                                # 1757 passed, 1 pre-existing failure
make format, make lint, make security           # clean
uv run pre-commit run --files strix/interface/tui/runtime.py tests/test_go_tui_runtime.py
                                                # every hook passes

make type-check runs mypy, which is clean here, and pyright, which reports 864 errors across strix/. That count is identical on this branch and on a clean main.

The suite failure is tests/test_pricing.py::test_resolves_common_bare_model_names. It fails the same way on a clean main (#1213, PR #1215) and this branch does not touch it.

Note

run_strix_scan's teardown is open to the same shape from any other caller that cancels twice. cli.py installs a SIGINT handler that calls sys.exit on every signal, so a second Ctrl+C during teardown looks like it would interrupt the delete the same way. I did not reproduce that one and left it out to keep this change to the reported bug. Happy to send a follow-up that makes the teardown itself cancel-proof if you want it covered.

The sidecar sends app.quit and exits in the same batch, so the command
handler and run()'s finally both call quit(). The second cancel lands on
the docker delete the first one's teardown is awaiting, and the sandbox
container is left running.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QE1brKHrPWcD8SHYooGBA1
@poison-control
poison-control force-pushed the fix/sandbox-leak-on-quit branch from de5338f to cf18972 Compare September 18, 2026 16:18
@poison-control poison-control changed the title fix(tui): tear the sandbox down when ctrl+q quits twice fix(tui): tear the sandbox down when Ctrl+Q quits twice Sep 18, 2026
@poison-control
poison-control marked this pull request as ready for review September 18, 2026 16:21
@greptile-apps

greptile-apps Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the idempotent shutdown path preserves sandbox cleanup under the duplicate Ctrl+Q quit sequence.

Summary

This PR makes TUI shutdown idempotent so the duplicate quit paths triggered by Ctrl+Q cannot cancel sandbox cleanup twice.

  • Stores the first shutdown operation as a shared task.
  • Shields that task from cancellation propagated by later waiters.
  • Adds a deterministic regression test that holds teardown open while both quit paths run.

Reviews (1) · Last reviewed commit: "fix(tui): tear the sandbox down when Ctr..."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Automatically delete container

1 participant