Investigating "weird redraw issues" surfaced by the wezterm-term swap
turned up four separate bugs in the proxy↔terminal byte path:
1. TerminfoRenderer caches CellAttributes across render_to calls.
The trailing raw `\x1b[0m` and out-of-band CLEAR_SCREEN writes
left its `current_attr` desynced from the real terminal — next
frame's SGR transitions were computed against the wrong baseline,
manifesting as bg-color smears and sticky links. Route the reset
through `Change::AllAttributes(default)` so the renderer's pen
stays in sync, and recreate the renderer whenever `force_full`
fires (startup, lookback exit, alt exit, resize) so the cached
state can't outlive an out-of-band write.
2. Proxy startup didn't clear the terminal, so the first VT diff
landed on top of whatever the user's shell had on screen. Wipe
on spawn alongside enabling bracketed paste.
3. `Drop` only disabled bracketed paste and restored termios, so an
exit mid-render leaked active styles into the shell prompt and
left Claude's last UI frame on screen. Add OSC-8 close → SGR 0 →
CLEAR_SCREEN → CURSOR_HOME before the existing teardown.
4. Under tmux, steady-state diff renders produced a palimpsest of
stacked frames because termwiz's `Surface::diff_lines` only walks
the new frame's *visible* cells — cells that became blank don't
generate clearing Changes. tmux is also tracking the grid and is
sensitive to the partial repaint stream. Detect `$TMUX` at spawn
and switch to passthrough: feed wezterm-term for state but
forward raw child bytes to stdout. Lookback's one-shot full
repaint still uses the renderer (screen is cleared first, so
the diff_lines blank-cell behavior is fine).
Test infrastructure:
- New shared `test_support::pty_mock_binary()` honors CARGO_TARGET_DIR,
tries debug/release, and panics with an actionable message instead
of returning Option<String>. All 8 `let Some(...) else { return; }`
silent-skip patterns replaced with direct destructure.
- Mirror is now advanced after `render_to` succeeds, not before.
New regression tests:
- `sgr_reemitted_on_second_frame_after_wire_reset`
- `hyperlink_reemitted_on_second_frame_after_wire_close`
- `forced_full_render_reemits_sgr_after_renderer_recreate`
- `test_lookback_exit_emits_clear_screen` asserts CLEAR/HOME precede
the SYNC-wrapped repaint payload.
122 passing / 0 failing / 4 ignored (the two pre-existing
underline-style cases plus the claude-CLI-required integration).