Skip to content

tui: background shell completion notices can accumulate below the active turn #63345

Closed as not planned

Description

@jarmenterosx2

Description

Intermittently, in the TUI, a finished background shell's Shell finished · <command> notice is appended after the active turn instead of folding into its tool row. When several of them land while the turn is still running, they stack below the Thinking/assistant row, the active turn is no longer the latest message, and the viewport follows the newest notice instead of the live output.

It does not happen on every turn that uses background shells; it appears randomly. I have not identified the exact trigger.

Frequency: intermittent (random); not consistently reproducible.

Plugins

alerta.ts, github-write-gate.ts, opencode-todo

OpenCode version

2.2.7

Steps to reproduce

  1. Use the TUI (WSL2) and send prompts that make the agent launch several shell commands with background: true while it keeps working.
  2. Repeat a few turns: the bug is intermittent; most turns do not reproduce it.
  3. When it happens, the Shell finished notices appear below the active Thinking/assistant row and keep accumulating as more background shells finish.

Screenshot

image

Operating System

Linux 6.18.33.2-microsoft-standard-WSL2 (WSL2, x86_64)

Terminal

Windows Terminal (WSL2)

Activity

  1. opencodejr commented on Nov 11, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report! I tried this on 2.2.7 with a simulated model: the agent starts three background shells, then runs one long step while they all finish.

    I saw the same picture as your screenshot: while the step was still running, the Shell finished rows stacked below the Thinking row. As far as I can tell, this is how the TUI is designed to work, not a bug:

    • A finished background shell's notice is queued for the agent and only handed to it at the start of its next step.
    • Until then, the TUI shows it at the bottom as pending input, the same way it shows a message you send while the agent is busy.
    • When the step ended, the notices moved into place after that step's output, and the agent's next step received them and continued. Nothing stayed stuck at the bottom.

    That's also why it seems random: it only happens when shells finish in the middle of a long step. Showing the notice inside its shell tool row isn't something the TUI does today; notices are always separate rows. If you'd like them drawn differently while they're pending (for example, never below the live turn), please open a feature request. #61732 is related.

    If you see notices that stay below the turn after the agent's next step has started, or the live output gets pushed off screen while it streams, please reply with a screenshot or recording of that and, if you can, your session settings in the TUI config. That would be a real bug and we'd look again.

  2. jarmenterosx2 commented on Nov 11, 2026

    @jarmenterosx2
    Author

    Confirmed: the older Shell finished rows accumulate and stay pinned at the bottom. There are already several of them from previous commands. When I send a new message, it appears at the bottom first, then the message and its answer move above those rows, and the old Shell finished rows stay at the bottom. In the recording: I start two background shells (sleep 5, sleep 7) and the agent answers the ocean question; then I send "Quick one: what's the capital of US? One word." and get "Washington"; both messages and answers end up above the old Shell finished stack, which remains below. The session inbox was empty and the session was idle, so those notices were delivered, not pending.

    The recording shows that sequence; the old command text is redacted. TUI settings: default — no cli.json; no custom session, tabs, scroll or keybinds settings. Version 2.2.7 on WSL2 / Windows Terminal.

    Screen recording:
    https://github.com/user-attachments/assets/a8d9e348-7c65-4403-a596-eea017be470c

  3. opencodejr commented on Nov 11, 2026

    @opencodejr
    Contributor

    Thanks for the recording and the extra detail. That helps a lot: notices that stay pinned after they've been delivered, with an idle session, are not the designed behaviour I described before.

    I tried your sequence on 2.2.7 with default TUI settings and no plugins:

    1. The agent starts sleep 5 and sleep 7 in the background, answers a question and ends its turn.
    2. Both shells finish while the session is idle. Each Shell finished row appears and the agent picks it up.
    3. I send "Quick one: what's the capital of US? One word." and get "Washington".

    In my run, the new message and its answer showed up after the Shell finished rows, in time order. Nothing stayed pinned at the bottom, so I couldn't reproduce it yet.

    The main differences from your setup are your plugins and a longer session. These would help narrow it down:

    • Plugins: does it still happen with alerta.ts, github-write-gate.ts and opencode-todo disabled, in a fresh session?
    • Reconnects: before the rows got stuck, did the TUI reconnect, or did the OpenCode service restart? WSL2 sleeping or the terminal being closed could cause this.
    • Restart: if you quit the TUI and reopen the same session, are the old Shell finished rows still at the bottom, or back in place?
    • Fresh session: can you get the stuck rows with the short sleep 5 / sleep 7 sequence alone, starting from a new session?
  4. jarmenterosx2 commented on Nov 11, 2026

    @jarmenterosx2
    Author

    Results (it has happened only 2–3 times, randomly):

    1. Reopening the same session: no — the transcript is back in the correct order, with nothing stuck at the bottom. So the stuck rows are not persisted; a TUI restart fixes them.
    2. Fresh session with the short sleep 5 / sleep 7 sequence: no, I couldn't reproduce it.
    3. I skipped the long-session-with-plugins-disabled test because it would spend model credit; I can't justify that for a random issue.

    Important: this is not tied to a restart or reconnect. The TUI had been running normally and the session had been working; the rows started sticking mid-session, with no interruption at that moment. It has happened 2–3 times, randomly, always in long sessions. Reopening the session puts the rows back in place, so it looks like a live TUI client state rather than the persisted transcript order.

  5. opencodejr commented on Nov 11, 2026

    @opencodejr
    Contributor

    Thanks for testing those, especially the reopen result. Since reopening fixes the order, the saved session is fine and the problem is in what the running TUI keeps in memory.

    I tried again with a long session on 2.2.7, using a simulated model and default settings: 12 turns, each starting two background shells, with steer messages sent mid-step and pauses where the shells finished while the session was idle. Every Shell finished row went into the right place, and later messages and answers showed up after them. I couldn't get them to stick.

    What you describe (new messages show up at the bottom first and then jump above the stuck rows) is how the TUI places input it thinks hasn't been delivered yet. My best guess is that the TUI sometimes keeps treating an already-delivered notice as pending. I haven't confirmed that and I don't know what triggers it, so I'll leave it for the maintainers to look at in the code.

    Until then, reopening the session puts the rows back in order. If it happens again, these would help:

    • A note of what was going on just before the first row got stuck. For example, whether you sent a message while the agent was working, or a shell finished right as a turn ended.
    • Whether one of your plugins sends messages into the session, or reacts to shell results.
    • The TUI log from around that time.
  6. opencodejr commented on Nov 12, 2026

    @opencodejr
    Contributor

    Thanks again for the recording and for testing the reopen and fresh-session cases. I tried three times on 2.2.7 to get the Shell finished rows to stick below newer messages: notices arriving during a long step, your sleep 5 / sleep 7 sequence, and a long session of many turns with mid-step steer messages and idle gaps. They went into the right place every time. Without a way to trigger it, I'm closing this issue for now.

    Until it's tracked down, reopening the session puts the rows back in order. If it happens again, please share what was going on just before the first row got stuck (for example, a message sent while the agent was working, or a shell finishing right as a turn ended), whether any of your plugins send messages into the session, and the TUI log from around that time. Replying here reopens the issue.

  7. Neko-Yukari commented on Nov 12, 2026

    @Neko-Yukari

    Added a deterministic v2.2.7 client-state reproduction and runnable test at #59615 (comment) . A stale inbox hydration response arriving after fresh transcript hydration reintroduces consumed user/synthetic input as pending, and the unchanged TUI reducer moves it below the assistant. Four race cases fail; eight ordering/delivery controls pass. No plugins are loaded in this fixture. This reproduces a client race consistent with the stuck-row symptom; the original live event sequence remains uncaptured.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/tuiTerminal interfaceplatform/wslWindows Subsystem for Linuxtype/bugSomething is brokenversion/v2OpenCode 2.x

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions