Skip to content

fix(web,mobile): show background task endings on the call that started them - #535

Merged
incognitojam merged 2 commits into
mainfrom
styal/background-task-endings
Sep 29, 2026
Merged

incognitojam merged 2 commits into
mainfrom
styal/background-task-endings

Conversation

@incognitojam

@incognitojam incognitojam commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Note

A background command's ending now shows on the Bash row that started it, instead of as a separate row in whatever turn it ends in. This also stops a stale notice from folding away the divider after /compact.

Problem

When a background task ends (a Bash command run in the background, a command moved to the background at its timeout, or a monitor), its end event gets the ID of whichever turn is running at that moment, and clients show it as its own row in that turn. The Bash row that started the command reads as finished as soon as the command moves to the background, so scrolling back up gives no sign of whether it finished, failed, or was abandoned.

When the provider process ends with a background command still running, Claude reports the task as stopped when the session next resumes ("Background shell command didn't finish before the previous session ended"). That row lands in the next turn. After a manual /compact, it counted as work, so the turn folded behind "Worked for …" and hid the compaction divider.

Change

Web and mobile match a background task's end event to the tool row whose call ID is the task's toolUseId, which Claude includes on every task event.

  • Where it shows: the tool row keeps its turn and position and shows how the task ended.
    • Web shows a check, cross or dash in the status spot setup actions already use, with the provider's summary as the tooltip.
    • Mobile adds the summary to the row's expanded detail.
    • A failed ending also marks the call failed.
  • Long foreground commands: when a task ends while its call is still running, the call's own result already covers it, so no mark is added and the duplicate task row next to the Bash row goes away.
  • Fallback: an end event whose launching row isn't loaded still shows as its own row.
  • Mobile tool rows: mobile used to hide a tool row whenever any task named it as its launcher. It now hides only calls that launched agents, so background commands keep their Bash row, as on web.

The shared outcome reader lives in packages/client-runtime/src/work-log/presentation.ts. The server is unchanged. The fork feature ledger records this as background-task-endings. Upstream's V2 orchestrator keeps background work in the run that started it (see pingdotgg#13789). On upstream main, pingdotgg#10295 proposed a fuller version of this and was closed while V2 is in progress.

Before and after

Before: after /compact, the stale "Task stopped" row is in the turn, which folds and hides the divider until expanded.

before: the compaction divider is inside the collapsed fold, after the stale background shell notice

After: the /compact turn contains only the compaction, so the divider shows directly.

after: /compact followed directly by the compaction divider

After: the Bash row from the earlier turn shows a stopped mark, with Claude's summary on hover.

after: the launching Bash row with a stopped mark and its tooltip

Validation

  • Real web client: a dev server on a copy of real data. The fixture thread holds the stretch of a real Claude thread from a Bash call started with run_in_background to a /compact the next day, with the stopped notice the CLI sent on resume.
    • /compact is followed directly by "Compacted context 286K → 8.48K tokens".
    • The earlier turn's group lists the vp run dev Bash row with a dash mark, and hovering shows the summary.
    • A task whose launching call is older than the fixture still shows as its own row.
    • No page or console errors.
  • Same rows through the web timeline derivation: the /compact turn yields one context-compaction row and no fold.
  • New web tests (session-logic.test.ts):
    • An ending in a later turn lands on the launching row and stays visible in its group.
    • A long foreground command keeps its own result.
    • An ending without its launching row stays as its own row.
    • A failed ending marks the call failed.
  • New mobile test (threadActivity.test.ts): the ending lands on the launching row in its original turn.
  • Existing suites: web session-logic, MessagesTimeline.logic and MessagesTimeline, mobile threadActivity, and client-runtime work-log all pass. Typecheck passes for web, mobile and client-runtime.

Not verified: mobile on a device or emulator (covered by the feed unit test only), and completed or failed endings on screen (covered by the unit tests).


Written by an agent (Claude Code, claude-opus-5-5).

…d them

A background task's end event (a backgrounded shell command or a monitor)
was stamped with whichever turn was running when it arrived and rendered
as its own row there. The Bash row that started it read as finished as
soon as the command went to the background, with no sign of how it
ended. A task left running when the provider session ended was reported
on resume inside the next turn. After a manual /compact, that stale row
counted as work, so the turn folded and hid the compaction divider.

Web and mobile now match a background task's end event to the tool row
whose call ID is the task's toolUseId. The row keeps its turn and
position and shows the ending: web adds a completed, failed, or stopped
mark with the provider's summary as its tooltip, and mobile adds the
summary to the expanded detail. A failed ending marks the call failed.
When the task ends while its call is still running (a long foreground
command), the call's own result stands and no mark is added. An end
event whose launching row is not loaded still shows as its own row.

Mobile hid a tool row whenever any task named it as its launcher; it
now hides only calls that launched agents, so background commands keep
their row as on web.
@incognitojam
incognitojam merged commit addca08 into main Sep 29, 2026
24 checks passed
@incognitojam
incognitojam deleted the styal/background-task-endings branch September 29, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant