Skip to content

fix(tui): keep session tab busy while background shell runs - #63676

Open
aniruddhaadak80 wants to merge 1 commit into
anomalyco:devfrom
aniruddhaadak80:fix/tui-background-shell-tab
Open

aniruddhaadak80 wants to merge 1 commit into
anomalyco:devfrom
aniruddhaadak80:fix/tui-background-shell-tab

Conversation

@aniruddhaadak80

@aniruddhaadak80 aniruddhaadak80 commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #62505

Type of change

  • Bug fix

What does this PR do?

A session tab showed no spinner while a background shell in that session was still running, even though the shell row in the transcript kept spinning.

SessionTabsProvider.status().busy only looked at the session's active state and its non-synthetic pending items. It never looked at data.shell, so once the turn went idle after a shell was backgrounded, the tab indicator took the non-runs() path and showed nothing. ShellDisplay kept spinning because it checks data.shell.get(id) directly.

The fix adds the running shells of the session family to the busy check:

data.shell.listBySession(id).some((shell) => shell.status === "running")

Shell entries are removed from the store on shell.exited / shell.deleted, so listBySession reflects only the shells that are still running. The same status() feeds createProgramStatus, so the terminal title / program status is corrected by the same change.

How did you verify your code works?

  • New test running background shell keeps its tab busy in packages/tui/test/context/session-tabs.test.ts: it emits shell.created with status: "running" for a background tab and asserts busy flips to true, then emits shell.deleted and asserts it flips back.
  • bun test test/context/session-tabs.test.ts -t "running background shell" → 1 pass, 0 fail.
  • tsc -b packages/tui → clean.

Screenshots / recordings

Not a UI change in the visual sense; the tab strip animation is the visible effect.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #51810, #48482, #56660

Copilot AI balanced review requested due to automatic review settings November 13, 2026 04:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix works. I reproduced the bug on the base by running !sleep 300 in the TUI with tabs on: the shell row and the 1 shell footer spin but the tab doesn't. With this change the tab shows the spinner (screenshots below). The session-tabs tests pass, the new test fails on the base as it should, and tsc -b is clean in packages/tui.

Public API: nothing changes in shape. The plugin API's tabs.list() now reports busy: true for sessions with running shells, and the terminal program status reports them as working.

One design question for maintainers (see the inline comment): every running shell owned by the session now makes the tab busy. That includes shell({ background: true }) with no timeout, which is how dev servers are started, and user ! shells. A session with a dev server would look busy, both in the tab and in the terminal title, for as long as the server runs. The open #60500 deliberately leaves explicit background shells out of "busy" for this reason, so these two pull requests need to agree on one rule.

This only covers the tab strip and terminal status. The /sessions list still uses its own running check in component/dialog-session-list.tsx, so #51810 stays open.

Related: #51810, #48482, #56660

Before

After

data.session.status(id) === "running" ||
data.session.pending.list(id).some((item) => item.type !== "synthetic"),
data.session.pending.list(id).some((item) => item.type !== "synthetic") ||
data.shell.listBySession(id).some((shell) => shell.status === "running"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This counts every running shell owned by the session, including shell({ background: true }) (no timeout, typically a dev server or watcher) and user ! shells (SessionShell.start, timeout: 0). Because createProgramStatus reads the same status(), a session with a dev server would show a spinning tab and a working terminal status for as long as the server lives. The open #60500 deliberately keeps explicit background shells out of busy for this reason and counts only auto-backgrounded work that will report back. Should this match that rule, e.g. skip shells started with background: true? Otherwise maintainers need to choose which behaviour they want.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tui: session tab shows no spinner while background shell still running

2 participants