Skip to content

fix(wrapper): zo continue --bypass-permissions kills the session on startup#97

Merged
SamPlvs merged 2 commits into
mainfrom
claude/tmux-liveness-grace
Jun 1, 2026
Merged

fix(wrapper): zo continue --bypass-permissions kills the session on startup#97
SamPlvs merged 2 commits into
mainfrom
claude/tmux-liveness-grace

Conversation

@SamPlvs
Copy link
Copy Markdown
Owner

@SamPlvs SamPlvs commented Jun 1, 2026

Problem

zo continue in tmux mode (zo continue --repo . --bypass-permissions) launched the lead session correctly — logs show TUI ready after 5sLaunched lead session in tmux pane=… — and then logged Lead session completed, agent window closed ~15ms later (…49.583…49.598), killing the window. The user saw no Claude session and "Session completed" immediately.

Root cause

LifecycleWrapper._wait_tmux polled liveness with zero startup grace and zero debounce:

  • The first poll fires milliseconds after launch, before Claude's TUI has fully claimed the pane (and while Claude's one-time workspace-trust dialog for the newly --add-dir'd delivery repo can still be up).
  • A single negative reading (not pane_exists or not claude_running) immediately concluded the session had ended and tore the window down.

So any transient — startup race, trust dialog, momentary tmux display-message hiccup, brief foreground flip — was indistinguishable from a real /exit. (The same binary had run fine for 4+ hours earlier in the day once the directory was already trusted, which is why it was intermittent.)

The launch path already polls carefully for readiness (_wait_for_tui_ready requires stable content); the completion path trusted an instantaneous first reading. That asymmetry was the bug.

Fix

  • _STARTUP_GRACE_POLLS=2 — ignore negative readings for the first polls (~20s at the 10s interval); never conclude "exited" during startup.
  • _DEAD_CONFIRM_POLLS=2 — require consecutive negatives before concluding the session ended (debounce).
  • _DEAD_RECHECK_INTERVAL=2.0 — re-check quickly while confirming a suspected exit, so a genuine /exit is still detected promptly rather than after a full poll interval.

Tests

  • Updated test_tmux_wait_completes_when_pane_closes (was asserting complete-on-first-negative) → now expects confirmed-exit on the 4th poll.
  • Added test_tmux_wait_ignores_negative_during_startup_grace — regression for the 15ms teardown.
  • Added test_tmux_wait_debounces_transient_negative — a single post-grace negative followed by a live reading must not end the session.

53 wrapper tests pass, ruff clean, validate-docs.sh 0 failures. Self-evolution prior PR-042 added; STATE.md + DECISION_LOG.md updated.

🤖 Generated with Claude Code

`zo continue` in tmux mode launched the lead session correctly, then
logged "Lead session completed, agent window closed" ~15ms later and
killed the window — leaving the user with no visible Claude session.

Root cause: `LifecycleWrapper._wait_tmux` polled liveness with zero
startup grace and zero debounce. Its first poll fired milliseconds
after launch — before Claude's TUI had claimed the pane, and while a
one-time workspace-trust dialog for the newly --add-dir'd delivery
repo could still be up — and a single negative reading (`not
pane_exists or not claude_running`) immediately concluded the session
had ended. Any transient (startup race, trust dialog, momentary tmux
query hiccup, brief foreground flip) was indistinguishable from a
real /exit.

Fix:
- `_STARTUP_GRACE_POLLS=2`: ignore negative readings for the first
  polls (~20s at the 10s interval) so startup never looks like exit.
- `_DEAD_CONFIRM_POLLS=2`: require consecutive negatives before
  concluding the session ended (debounce transients).
- `_DEAD_RECHECK_INTERVAL=2.0`: re-check quickly while confirming a
  suspected exit so a genuine /exit is still detected promptly.

Tests: updated the test that asserted complete-on-first-negative to
expect confirmed-exit-on-4th-poll; added regressions for the 15ms
teardown (startup grace) and transient-negative debounce. 53 wrapper
tests pass, ruff clean, validate-docs 0 failures. PR-042 in PRIORS.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Jun 1, 2026

Deploying zero-operators with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6c414fb
Status: ✅  Deploy successful!
Preview URL: https://a6ea2573.zero-operators.pages.dev
Branch Preview URL: https://claude-tmux-liveness-grace.zero-operators.pages.dev

View logs

…alog

Root cause of "zo continue --bypass-permissions kills the session": the
bypass overlay sets defaultMode=bypassPermissions, which makes Claude
Code 2.1.159 show a one-time interactive consent dialog on startup:

    WARNING: Claude Code running in Bypass Permissions mode
      > 1. No, exit
        2. Yes, I accept

`_wait_for_tui_ready` mistakes this dialog for the ready TUI (it is
~1231 chars and stable), so the launcher pastes the 24KB lead prompt
and presses Enter — which confirms the default-highlighted "No, exit".
Claude quits on startup, the pane falls back to bash, and `_wait_tmux`
correctly tears the window down. The lead session's own transcript
(killed mid-Bash-tool-call, not crashed) and live instrumentation of
the real launch path confirmed the mechanism.

The prior commit's grace+debounce only delayed the death (15ms -> 22s);
it never stopped Claude from exiting. It stays as defense-in-depth.

Fix: `ensure_bypass_disclaimer_accepted()` persists
`bypassPermissionsModeAccepted: true` to ~/.claude.json (idempotent,
preserves all keys, refuses to clobber corrupt JSON). Called from
`_launch_tmux` and `_launch_headless` when bypass is requested —
passing --bypass-permissions is the user's consent, so recording it is
faithful to intent. With the flag set, Claude boots straight into the
working TUI; verified Claude stays alive 32s (past the old 22s death).

+4 overlay tests; 53 wrapper + 16 overlay tests pass, ruff clean,
validate-docs 0 failures. PR-043 in PRIORS.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@SamPlvs SamPlvs changed the title fix(wrapper): add startup grace + debounce to tmux liveness detection fix(wrapper): zo continue --bypass-permissions kills the session on startup Jun 1, 2026
@SamPlvs SamPlvs merged commit e3b131e into main Jun 1, 2026
5 checks passed
@SamPlvs SamPlvs deleted the claude/tmux-liveness-grace branch June 1, 2026 10:43
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.

1 participant