Three bugs my AI agents couldn't fix A developer building wimux, a terminal multiplexer for Windows in Rust, found that AI agents could not fix three bugs caused by interactions between ConPTY, xterm.js, and the server-side grid. The bugs were resolved by debouncing resize events and building a reproduction harness to isolate focus issues, highlighting the need for cross-layer debugging. I spent the last few weeks building wimux https://github.com/fabperso/wimux , a terminal multiplexer for Windows written in Rust. tmux and zellij are Unix-first — on Windows they only really live inside WSL, detached from the native shell. I wanted persistent PowerShell sessions, so I wrote them. Most of the code was written by AI agents. My commits say so explicitly Co-Authored-By: trailers , and I'd rather lead with that than have someone discover it. What I want to write about isn't the code the agents produced — it's the three bugs they couldn't fix, because those turned out to be the interesting part. A quick sketch of the thing, so the bugs make sense: a detached per-user daemon owns the sessions, drives child processes through ConPTY portable-pty , and emulates the terminals server-side. Clients — a TUI, a Tauri v2 GUI running xterm.js, and a scriptable CLI — attach over a Windows named pipe with a postcard binary protocol. So: a Rust daemon, a WebView, and a PTY, three layers that each have their own idea of what the screen looks like. Symptom: split a pane while a full-screen TUI app was running, and the display filled with orphaned characters — box-drawing glyphs stranded from a narrower render. Everything pointed at the client. xterm.js is the thing drawing pixels; the GUI reparents DOM nodes when the layout changes; re-attaching to the session made the corruption disappear. Obvious client bug. We tried four fixes, in this order: Four plausible fixes, four failures. Each one was a reasonable theory, and each one was aimed at the client. The move that cracked it took thirty seconds: I captured the terminal grid from the server , using the daemon's own view of the pane — the one that has never heard of xterm.js. wimux agent capture -t 0 -p 2 The orphaned glyphs were there too. That single output eliminated the entire client. The corruption existed before a single pixel was drawn. The real cause was upstream of everything we'd been touching: splitting a pane sends a burst of intermediate sizes to the PTY. The app inside redraws at each one, its incremental redraws don't clear what they no longer cover, and the leftovers stay in the grid — in the server's grid. The fix was three lines: debounce resize events so the PTY only ever sees the final, settled size. The lesson isn't "debounce your resizes." It's that four consecutive fixes failing is data. It means the assumption underneath all four is wrong — and no amount of new fixes at the same layer will help. Go get an observation from a different layer instead. Symptom: click a pane, type — the first keypress doesn't appear, and the machine beeps. Press again and it works. This one resisted for days, partly because every reasonable theory was wrong. Here's what I eliminated, and how: mousedown → term.focus . The first keypress went through, every time. GetForegroundWindow and GetGUIThreadInfo while typing. Focus never moved. document.hasFocus showed all keystrokes landing on the terminal's textarea. The blur/focus events I'd blamed were me alt-tabbing. ESC I on focus, PSReadLine chokes on it, beeps, and eats the next character. It explains every symptom. I enabled the mode and watched what xterm actually emitted: nothing. ConPTY had swallowed the sequence. Five theories, five refutations, all backed by an observation rather than an argument. What finally worked was building a reproduction harness : a script that clicks a pane and sends a keystroke after a delay, sweeping the delay from 0 ms to 150 ms, dozens of times. A human can't hit a 50 ms window on purpose. A script does it forty times in a row. Result: zero losses. Clicking inside a pane was never the problem. So I pointed the harness at the sidebar instead — clicking a session entry to switch workspaces. Four out of four keystrokes lost , with a log that made the mechanism unmistakable: MD pane=- target=SPAN.name active=TEXTAREA.xterm-helper-textarea == BLUR == the terminal loses focus KEY "k" active=BODY target=BODY the keystroke lands on