diff --git a/claude_bottle/backend/smolmachines/bottle.py b/claude_bottle/backend/smolmachines/bottle.py index d2eb01b..3ecc828 100644 --- a/claude_bottle/backend/smolmachines/bottle.py +++ b/claude_bottle/backend/smolmachines/bottle.py @@ -26,14 +26,10 @@ from . import pty_resize as _pty_resize from . import smolvm as _smolvm -# Absolute path to the pty_resize wrapper. The dashboard's tmux -# pane (split-window / respawn-pane) opens the new pane in its -# OWN cwd, not the cwd of the process running split-window — so -# invoking the wrapper as `python -m ` would fail -# with ModuleNotFoundError whenever the operator's tmux pane was -# started from anywhere outside the claude-bottle repo. Absolute -# path sidesteps the cwd dependence (the wrapper has no -# claude_bottle.* imports, so it runs as a standalone script). +# Absolute path to the pty_resize wrapper. Invoke as +# `python ` rather than `python -m ` so the +# wrapper runs regardless of cwd / sys.path — it has no +# claude_bottle.* imports, so it's self-contained. _PTY_RESIZE_SCRIPT = _pty_resize.__file__ diff --git a/claude_bottle/backend/smolmachines/pty_resize.py b/claude_bottle/backend/smolmachines/pty_resize.py index ae22804..5603ca0 100644 --- a/claude_bottle/backend/smolmachines/pty_resize.py +++ b/claude_bottle/backend/smolmachines/pty_resize.py @@ -132,23 +132,9 @@ def main(argv: list[str]) -> int: signal.signal(signal.SIGWINCH, sync) proc = subprocess.Popen(inner) - # Defer the initial sync. Firing it immediately races - # libkrun's per-exec OCI config write: both `smolvm machine - # exec` invocations stash a config.json in the same smolvm - # state dir during their bringup window, libkrun loads one - # mid-write, and the main exec dies with SIGKILL (rc=137) - # or libkrun's "parse error: trailing garbage" depending on - # scheduling. Trivial inner commands finish before the - # overlap matters; claude's slower startup hits the race - # every time, only inside tmux (the outside-tmux foreground - # handoff path takes a different bringup sequence that - # happens to dodge the window). - # - # A 2s timer is past the bringup window on a warm VM, so - # the side-channel writes a fresh config.json without - # collision, and the in-VM PTY is sized before claude has - # finished rendering its first frame. daemon=True so the - # timer doesn't block exit when the child finishes quickly. + # Initial sync is deferred — see _STARTUP_SYNC_DELAY_SEC. + # daemon=True so the timer doesn't block exit when the child + # finishes before the delay elapses. timer = threading.Timer(_STARTUP_SYNC_DELAY_SEC, sync) timer.daemon = True timer.start()