From 9c83ea64287b0bde1b567b3af6c7714f40ef0bb7 Mon Sep 17 00:00:00 2001 From: claude Date: Wed, 27 May 2026 20:47:32 -0400 Subject: [PATCH] chore(smolmachines): re-add pty_resize debug log (temp, for issue diagnosis) User reports the launch still crashes in tmux after b9853ae's stdin=DEVNULL fix. Re-instrument to capture the next failure mode (argv, ppid, sync size, child exit, Popen tracebacks). Removable once the inside-tmux launch is confirmed stable. Co-Authored-By: Claude Opus 4.7 --- .../backend/smolmachines/pty_resize.py | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/claude_bottle/backend/smolmachines/pty_resize.py b/claude_bottle/backend/smolmachines/pty_resize.py index a7e9418..2d27ab8 100644 --- a/claude_bottle/backend/smolmachines/pty_resize.py +++ b/claude_bottle/backend/smolmachines/pty_resize.py @@ -35,12 +35,28 @@ follow-up tracked separately).""" from __future__ import annotations +import datetime import fcntl +import os import signal import struct import subprocess import sys import termios +import traceback + + +_DEBUG_LOG_PATH = os.path.expanduser("~/.claude-bottle/pty_resize.log") + + +def _log(msg: str) -> None: + try: + os.makedirs(os.path.dirname(_DEBUG_LOG_PATH), exist_ok=True) + with open(_DEBUG_LOG_PATH, "a") as f: + ts = datetime.datetime.now().isoformat(timespec="milliseconds") + f.write(f"[{ts} pid={os.getpid()}] {msg}\n") + except OSError: + pass def _read_winsize() -> tuple[int, int] | None: @@ -100,35 +116,42 @@ def main(argv: list[str]) -> int: We don't use argparse — the `--` separator is the contract and everything past it is forwarded verbatim. Keeps the wrapper transparent for callers building argv programmatically.""" + _log(f"start argv={argv!r} TMUX={os.environ.get('TMUX','')!r} " + f"ppid={os.getppid()}") + if len(argv) < 3 or argv[1] != "--": sys.stderr.write( "usage: python -m claude_bottle.backend.smolmachines.pty_resize " " -- \n" ) + _log("exit=2 (bad argv)") return 2 machine = argv[0] inner = argv[2:] def sync(*_args) -> None: size = _read_winsize() + _log(f"sync size={size!r}") if size is None: return _push_size(machine, *size) - # Install BEFORE spawning the child so the first SIGWINCH - # (e.g., from tmux refreshing the pane right after respawn) - # is caught even if it races the initial sync. signal.signal(signal.SIGWINCH, sync) - proc = subprocess.Popen(inner) + try: + proc = subprocess.Popen(inner) + except BaseException: + _log("Popen failed:\n" + traceback.format_exc()) + raise + _log(f"child pid={proc.pid}") sync() # push initial size — VM PTY starts at 0 0. while True: try: - return proc.wait() + rc = proc.wait() + _log(f"child exit rc={rc}") + return rc except KeyboardInterrupt: - # Ctrl-C in the operator's terminal → forward to the - # child once, then keep waiting. claude handles its - # own interrupt cleanup. + _log("KeyboardInterrupt → forward SIGINT to child") proc.send_signal(signal.SIGINT)