From 7649587f9aa90d1cabe910486021cd30878b3c8f Mon Sep 17 00:00:00 2001 From: Harry Bayliss Date: Thu, 14 May 2026 17:22:55 +0100 Subject: [PATCH] updated TODO --- TODO.md | 119 ++------------------------------------------------------ 1 file changed, 3 insertions(+), 116 deletions(-) diff --git a/TODO.md b/TODO.md index 8742472..cbe6a9c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,122 +1,9 @@ -- [ ] Switch-back rendering is wrong for diff-based TUIs (specifically codex / ratatui). Partial progress; deeper investigation needed — details below in "Switch-back render divergence". - [ ] Killed agents are visible in the command palette. They shouldn't be. - [ ] claude failed to connect to patterm mcp -32601 - [ ] codex doesn't show the patterm mcp at all - [ ] opencode doesn't show the patterm mcp at all - [ ] Open agents/processes should appear above the option to open a new one in the palette - [ ] Some sort of macros in the command pallete would be nice, like if i type `sw ` it would only show the switch entries. Maybe we should have info text greyed out to show these macros. - ---- - -## Switch-back render divergence - -### Symptom - -Switching focus to codex (and back to it again after another tab) leaves -codex's input box rendered wrong. The input text and the `›` prompt -glyph appear on different rows. Typing more characters in codex makes -the box "grow" to 4–5 rows tall even though the content is one short -line. Claude (claude-code, ink-based) is mostly fine after the fixes -below; codex (Rust/ratatui) is not. - -Initial spawn of codex looks correct. The bug only appears after a -focus switch off codex and then back. - -### What's already fixed and committed - -These actually helped; don't undo them blindly. - -1. **`cursorShifter` empty-`\x1b[r` bug** (`internal/app/cursorshift.go`) - — `\x1b[r` (reset DECSTBM) was being parsed as `(1,1)` and shifted to - `\x1b[4;4r`, producing a one-row scrolling region that scroll-exploded - the snapshot. Now rewrites empty params to `\x1b[;r` - in host coords. `cursorShifter` carries `childRows` for this. Test: - `TestCursorShifterDECSTBMEmptyResetsToViewport`. - -2. **Host-state reset prelude in `repaintFocused`** (`internal/app/app.go`) - — before replaying, write `\x1b[0m\x1b[?6l\x1b[;r\x1b[?25h\x1b[;H` - directly to stdout to clear leftover SGR / DECOM / DECSTBM from the - previously-focused child. - -3. **Use `SerializeVT` instead of plain text for the snapshot** - (`internal/app/app.go: repaintFocused`) — previously `repaintFocused` - used `SnapshotChild` (plain text, no SGR). Now it feeds - `SerializeChild` bytes through the per-focused-child viewport - renderer, preserving colors and cursor state. - -4. **Re-emit cursor as a child-space CUP through the renderer** - — `SerializeVT`'s output order is: content with CRLFs, `\x1b[0m`, - cursor CUP, **DECSTBM**, tabstops. DECSTBM has a documented side - effect of moving the cursor to the scrolling region's home, and the - trailing tabstop setup uses CHA (`\x1b[NG`) which leaves the - renderer's internal `vr.col` parked at the last tab-stop column. - Without a fixup the host cursor and the renderer's tracking both - drift. The current code re-emits the saved cursor as a child-space - `\x1b[;H` through the renderer, so the shifter writes the - right host CUP and `trackCSI` updates `vr.row`/`vr.col`. - -5. **`NudgeRedraw` on the focused child after replay** - (`internal/app/child.go: NudgeRedraw`, called via `defer` in - `repaintFocused`) — toggles PTY winsize by one row and back to force - the kernel to emit `SIGWINCH`. Intent: make ratatui throw away its - internal "last frame" diff state and emit a full frame. After this - change the initial load and the post-interaction state of codex are - visually equivalent, but both are still wrong. - -### What's still broken - -After all of the above, codex's input box still draws with the input -text and the `›` prompt on different rows, and "asdasdasdasd"-style -typing makes the box grow vertically instead of staying single-line. - -Suspected causes, in rough order of likelihood: - -- **The renderer is over-shifting some row-positioning sequence that - ghostty's `SerializeVT` emits but I haven't recognised.** Run the - probe pattern below to see what bytes go through. Pay special - attention to anything that targets rows after the DECSTBM is in - place, anything that uses DECOM, and any `\x1bD`/`\x1bM` (IND/RI) - which scroll within the region. -- **Ratatui's internal "previous_buffer" isn't actually getting reset - by `SIGWINCH`** in this PTY environment, or it's getting reset to a - size that doesn't match the emulator's. The one-row toggle in - `NudgeRedraw` might be a bad idea — try direct `kill(pid, SIGWINCH)` - with no size change (the kernel's `TIOCSWINSZ` skips SIGWINCH when - the size is unchanged, so we'd need to send the signal explicitly). - See `Child.signal` for the helper. -- **`childRows`/`childCols` reported via `TIOCGWINSZ` isn't what codex - expects.** If codex reads winsize at startup and caches it, our - `tabBarRows` change (1 → 3) might have left the cached size stale - in some path. Verify by spawning codex fresh after the chrome - change and confirming `stty size` inside codex matches - `layout.childCols()` × `layout.childRows()`. - -### Investigation tools - -- `internal/vt/probe_test.go` doesn't exist any more; recreate it to - print `SerializeVT` output for representative cases. The relevant - call is `(*GhosttyEmulator).SerializeVT()`. Confirmed shape: - ``` - \x1b[0m\x1b[;H\x1b[;r\x1b[3g\x1b[NG\x1bH... - ``` -- Add a debug tee around `viewportRenderer.Render` to log the raw - bytes codex emits **after** the snapshot replay. That will show - whether codex is emitting CUPs that target wrong rows (suggesting - its diff state is wrong) or whether it's emitting reasonable CUPs - and the renderer is mis-shifting them. -- The user said they're building a harness so agents can iterate on - this without manual screenshotting; once that exists, the diagnose - loop is: replay snapshot → capture host stdout → diff against - expected. Start with the simplest reproduction: spawn codex, switch - away, switch back, type one character, compare host bytes against a - golden file. - -### Files touched (so the next agent knows what to read) - -- `internal/app/app.go` — `repaintFocused` -- `internal/app/cursorshift.go` — DECSTBM handling, `childRows` -- `internal/app/viewport_renderer.go` — plumbing for `childRows` -- `internal/app/child.go` — `NudgeRedraw` -- `internal/app/cursorshift_test.go` — DECSTBM reset coverage -- Probe what `(*GhosttyEmulator).SerializeVT()` emits — that's the - source of truth for what we're replaying. + - [ ] sw = switch + - [ ] k = kill + - [ ] sp = spawn