Skip to content

Ignore background tasks that workflow agents start in the Claude bridge - #2434

Merged
SawyerHood merged 1 commit into
mainfrom
bb/fix-hidden-background-commands-thr_i7pnmrmq4r
Aug 25, 2026
Merged

Ignore background tasks that workflow agents start in the Claude bridge#2434
SawyerHood merged 1 commit into
mainfrom
bb/fix-hidden-background-commands-thr_i7pnmrmq4r

Conversation

@SawyerHood

Copy link
Copy Markdown
Collaborator

Human comments

What was wrong

Background commands that Claude Code workflow agents ran showed up in the parent thread's "Running background command" card and sidebar count, as if the parent had run them. The CLI emits task_started for every task in the session, including tasks a workflow agent starts, but it forwards tool_use blocks only from the main loop and from Agent sub-agents. task_started carries only tool_use_id (the child's own call), with no parent attribution. The bridge keyed the task on that id, the assembler minted a parent id that no item ever claimed, and the timeline projection treated the orphan as a root-level command. The same task_started also emitted turn.open, so a workflow child's command opened a provider-only turn in the parent that stayed open until the CLI's next result.

Evidence from one workflow-heavy thread: 2,971 of 3,052 local_bash tasks pointed at a parent call that never appeared in the log; 0 tool calls nested under its 73 Workflow calls (vs. 1,945 under Agent delegations); 100 of its 124 turns were opened by a stray task_started and lasted 500–1,900 s each. Orphaned local_agent tasks from workflow children also held the parent turn open through hasCompletionBlockingClaudeTasks.

What changed

  • plugins/provider-claude-code/src/task-translation.ts: translateClaudeTaskMessage takes hasForwardedToolUse. A task_started whose tool_use_id the bridge never saw open, and that is not a restart of a tracked task, is not materialized: no row, no parentRef, no turn.open. Its later progress/notification events fall through the existing unknown-task branches.
  • plugins/provider-claude-code/src/delta-translation.ts: wires the check to state.startedTools.has(id).
  • plugins/provider-claude-code/src/delta-test-harness.ts: spawningToolUseMessage / spawningToolUseFor helpers; existing hand-built task_started tests now prime the spawning tool_use as the real stream does.

No HOST_DAEMON_PROTOCOL_VERSION bump: the wire format is unchanged; the daemon only emits fewer events. The thread-view projection fallback (isDirectBackgroundTaskForCurrentAgent treating a missing parent as direct) is untouched; the orphans are removed at the source.

The ordering invariant (spawning tool_use streams before task_started, and before the call's tool_result) holds in every committed fixture and recording, in a live CLI 2.1.245 run (backgrounded Bash: tool_usetask_startedtool_result; foreground Bash emits no task_started), and in a live resume run (SendMessage to a finished agent emits a new task_started whose tool_use_id is the new SendMessage call). Mining 32 recent Claude Code thread logs (≈8,300 background tasks): all 7,051 orphans sit in threads that called Workflow, none in the 22 workflow-free threads; among 1,266 legitimate tasks there were 0 cross-turn parents, 0 results-before-task, and 0 task-first orderings.

How you verified

  • New test ignores tasks spawned by an unforwarded child (workflow agent) in task-translation.test.ts: fails before (a row and a turn/started are emitted), passes after. It also checks that a child local_agent does not block the turn and that the parent's own next background command still materializes.
  • pnpm exec turbo run test --filter=bb-plugin-provider-claude-code: 334 pass. The one failure is recorded/fork in bridge.recorded-conformance.test.ts, which fails identically on the unmodified branch on this machine and whose recording contains no task messages.
  • pnpm exec turbo run typecheck --filter=bb-plugin-provider-claude-code: pass. Prettier clean.
  • EAP codename scan: clean for the working tree and the push range.

Related: #2224 (the background_tasks_changed debug rows; not changed here).

AGENT GENERATED

The CLI reports every background task on the parent session, including
tasks a workflow agent starts, but it never forwards that agent's tool_use
blocks. The bridge keyed the task on tool_use_id alone, so the assembler
minted a parent id no item ever claimed, the projection treated the
orphan as the parent's own command, and task_started opened a
provider-only turn that stayed open until the CLI's next result.

The bridge now drops a task_started whose tool_use_id it never saw open
(and that is not a restart of a tracked task). Tests prime the spawning
tool_use as the real stream does, and a regression test covers the
unforwarded-child case.

Co-Authored-By: Claude <noreply@anthropic.com>
@SawyerHood
SawyerHood merged commit bf9aac2 into main Aug 25, 2026
15 checks passed
@SawyerHood
SawyerHood deleted the bb/fix-hidden-background-commands-thr_i7pnmrmq4r branch August 25, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant