Planned detailed GitHub issue analysis
Public
Planned detailed GitHub issue analysis
Agent
Pi
Model
GPT-5.2 Codex
Started
Jan 26, 2:39 PM
Messages4
Downloads0
Reviewed user instructions for issue analysis.
PlanOutlined approach for bug and feature request handling.
PlanAnalyze GitHub issue(s): https://github.com/badlogic/pi-mono/issues/954
For each issue:
Read the issue in full, including all comments and linked issues/PRs.
For bugs:
For feature requests:
Do NOT implement unless explicitly asked. Analyze and propose only.
Planning GitHub issue retrieval
I'm preparing to read an issue using the GitHub API via a bash script for integration.
>gh issue view 954 --json title,body,comments,labels,state{"body":"### What happened?\n\n## AUTHORSHIP Notice\n\nI, the human being algal, ran into this issue when using Clawdbot, when I noticed that early sections in AI responses were rendered and then erased in the output. I noticed that this was happening to sections preceding tool calls, in both Ghostty and in iTerm2, and both inside and outside of tmux\n\nI then used Codex-5.2 to analyze the cause of the problem, and to generate a minimal isolated reproduction of the behavior. I ran the repro script and observed (in iTerm2) what does indeed seem like buggy behavior -- namely, that the text available in the scrollback depends on the height of the terminal window at the time you run the script. \n\nSo I personally observed the original bug in clawdbot, and in the repro script included. But the bulk of the analysis, and nearly all of the copy in the following sections, and the repro script itself, was authored by Codex-5.2. I do not comprehend it in detail. (The \"nearly\" is because I restated the repro instructions myself to be very concrete about exactly what I did.) \n\nI know there's a lot of slop contributions going around, so I wanted to be transparent about this in case you feel it's not worth the time to engage with an issue report of this kind. I also would welcome any feedback or instructions on how to engage more helpfully.\n\n## Summary\n\nWhen streaming content that includes a tool call (text → tool → more text), the pre-tool text is overwritten when post-tool text renders. This happens when content exceeds the terminal viewport height.\n\n## Expected behavior\n\n- PRE-TOOL lines remain visible in scrollback above tool output\n- POST-TOOL lines append after tool output without overwriting earlier content\n\n## Actual behavior\n\nWhen content exceeds the viewport and new lines arrive after a tool-call pause, some earlier PRE-TOOL lines near the bottom are overwritten by POST-TOOL lines.\n\n## Environment\n\n- Terminal: Ghostty, iTerm2 (terminal-independent)\n- tmux: With and without (tmux-independent)\n- Intermittent: Depends on viewport fullness and cursor position when tool completes\n\n## Reproduction\n\nThe attached `viewport-overwrite-repro.ts` reproduces this without an LLM.\n\n```bash\n# Run in a small terminal (8-12 rows) for reliable repro\n# From the pi-mono repo root:\nnpx tsx packages/tui/test/viewport-overwrite-repro.ts\n```\n\nOr use tmux for a controlled viewport:\n\n```bash\ntmux new-session -d -s tui-bug -x 80 -y 12\ntmux send-keys -t tui-bug \"cd /path/to/pi-mono && npx tsx packages/tui/test/viewport-overwrite-repro.ts\" Enter\ntmux attach -t tui-bug\n```\n\n**What to look for:** When POST-TOOL lines render, some earlier PRE-TOOL lines near the bottom vanish or appear replaced.\n\n## Root cause\n\n**`hardwareCursorRow` is treated as a content line index, but the terminal cursor can only move within the viewport.**\n\nOnce rendered content exceeds the terminal height, the terminal scrolls and the cursor position is constrained to the visible viewport. `CSI A/B` (cursor up/down) does **not** scroll; it clamps at the top/bottom of the viewport.\n\nThe code in `doRender()` uses `lineDiff = firstChanged - hardwareCursorRow` and emits `CSI B` to move down. When `firstChanged` is below the bottom of the current viewport (common when appending new lines), `CSI B` does not actually move the cursor, so the first newly-rendered line is written on the **current bottom line**, overwriting previous content.\n\n### Why it shows up after tool calls\n\n- Tool calls often add enough lines to fill/overflow the viewport\n- When streaming resumes, the next text chunk is appended as a new line at the bottom — exactly the case that `CSI B` cannot handle without scrolling\n- If tool output temporarily expands then collapses, `maxLinesRendered` stays high and can trigger a full clear on the next update, compounding the effect\n\n### Secondary contributor\n\n`maxLinesRendered` never shrinks (by design). After a tool result is collapsed or truncated, `maxLinesRendered` can be much larger than `newLines.length`. This makes `viewportTop` large and can trigger the `firstChanged < viewportTop` full-render path, which clears scrollback (`ESC[3J`) and makes earlier text disappear.\n\n## Proposed fix direction\n\nTranslate to viewport coordinates and explicitly scroll when the target line is below the viewport:\n\n1. Compute `prevViewportTop` before rendering\n2. When moving to `firstChanged`, map both current cursor and target to **viewport rows**\n3. If target is below viewport, scroll by emitting newlines at the bottom before rendering\n4. Update `positionHardwareCursor()` to use viewport coordinates consistently\n\nPseudocode for the cursor movement fix:\n\n```typescript\nconst prevViewportBottom = prevViewportTop + height - 1;\nlet currentRow = Math.max(0, Math.min(height - 1, this.hardwareCursorRow - prevViewportTop));\n\nif (firstChanged > prevViewportBottom) {\n // Scroll to bring target into view\n const scroll = firstChanged - prevViewportBottom;\n const toBottom = (height - 1) - currentRow;\n if (toBottom > 0) buffer += `\\x1b[${toBottom}B`;\n buffer += \"\\r\\n\".repeat(scroll);\n currentRow = height - 1;\n}\n\nconst targetRow = Math.max(0, Math.min(height - 1, firstChanged - prevViewportTop));\nconst rowDelta = targetRow - currentRow;\nif (rowDelta > 0) buffer += `\\x1b[${rowDelta}B`;\nelse if (rowDelta < 0) buffer += `\\x1b[${-rowDelta}A`;\n```\n\nAn alternative is using absolute cursor positioning (`CUP`) within the viewport instead of relative `CSI A/B`, which avoids drift from incorrect `hardwareCursorRow` tracking.\n\n## Related\n\nIssue #814 (\"TUI flashes and delays write tool output when expanded content exceeds viewport\") was closed as unfixable. That issue mentions the `firstChanged < viewportTop` full-render path. This bug has a different trigger (tool call pause in streaming) but may share underlying causes.\n\n### Steps to reproduce\n\n1. cd to the root of pi-mono\n2. copy this repro script into the path `packages/tui/test/viewport-overwrite-repro.ts`.\n3. resize your terminal window to about 8 rows\n4. run this repro script by running: `npx tsx packages/tui/test/viewport-overwrite-repro.ts`\n5. after it prints \"POST-TOOL LINE\", scroll back and notice you can't see any of the PRE-TOOL LINEs\n6. reproduce the above steps but with a tall window, and notice that now you can see those lines\n\n[viewport-overwrite-repro.ts](https://github.com/user-attachments/files/24851337/viewport-overwrite-repro.ts)\n\n### Expected behavior\n\nI expect the window height not to affect what content is visible on scrollback.\n\n\n\n### Version\n\ncommit 82e3be9dc8422f461e979982bf869096bfe60cf8","comments":[],"labels":[{"id":"LA_kwDOPbFNk88AAAACHWTBxw","name":"bug","description":"Something isn't working","color":"d73a4a"}],"state":"OPEN","title":"Text before tool calls is overwritten when content exceeds viewport"}