Skip to content

feat: run parallel agents in linked threads - #6488

Draft
RedStar071 wants to merge 1 commit into
pingdotgg:mainfrom
RedStar071:t3code/parallel-agents
Draft

feat: run parallel agents in linked threads#6488
RedStar071 wants to merge 1 commit into
pingdotgg:mainfrom
RedStar071:t3code/parallel-agents

Conversation

@RedStar071

@RedStar071 RedStar071 commented Aug 13, 2026

Copy link
Copy Markdown

What Changed

Implements a minimal version of #5733: type /parallel <prompt> in a thread's composer to spawn
a linked sibling thread that works the sub-task concurrently, using whatever provider/model the
composer had selected when sent. The originating thread records a parallel-agent.started
activity when the child spawns and a parallel-agent.completed/interrupted/failed activity
when the child's turn settles (carrying its final assistant text), so results surface back into
the shared conversation. In Git projects, each parallel agent gets its own worktree branched from
the parent's current branch.

Adds an optional parentThreadId to the thread contracts, decider, and projector, a migration for
the new projection_threads.parent_thread_id column, the composer-side /parallel parsing and
spawn flow, and user/internals docs.

Why

There's currently no way to run multiple agents on independent sub-tasks within the same thread —
everything serializes through one agent, and users have to open separate threads and manually
merge results, losing shared context. This follows the "smallest useful scope" suggested in the
issue: threads spawned as parallel agents, linked back to the thread that spawned them, with their
outcome surfaced into the shared conversation.

UI Changes

/parallel is now listed alongside /model in the composer's slash-command menu. No screenshots
included — the change is additive to an existing menu and best seen live; happy to add
before/after captures if useful for review.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Model + harness: Claude Sonnet 5 via Claude Code.

Note

Add parallel agent support to run child threads linked to a parent thread

  • Introduces a /parallel <prompt> slash command in the chat composer that creates a child thread linked to the current thread as a parallel agent
  • Adds parentThreadId to thread contracts, projections, and persistence (migration 041 adds parent_thread_id column to projection_threads)
  • When a child thread's session write settles its running turn, the parent thread receives a thread.activity-appended event (parallel-agent.completed|interrupted|failed) with a preview of the final assistant message
  • Validates parent thread constraints in the decider: rejects self-parenting, cross-project parents, missing or deleted parents
  • Risk: existing thread records will have parent_thread_id = NULL after migration; decoders handle absence for backwards compatibility
📊 Macroscope summarized f0589cf. 16 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

Type /parallel <prompt> in a thread's composer to spawn a linked sibling
thread that works the sub-task concurrently with whatever provider/model
the composer had selected, so independent parts of a task can run side
by side instead of serializing through one agent. The originating thread
records when each parallel agent starts and finishes (with its final
response) so results surface back into the shared conversation, and each
agent gets its own Git worktree so simultaneous edits don't collide.

Adds an optional parentThreadId to the thread contracts/decider/projector,
a migration for the new column, and the composer-side spawn/parse logic
plus docs.

Model + harness: Claude Sonnet 5 via Claude Code.
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bd1e2c15-bb0c-4df2-a9c8-653ce235eaa4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 13, 2026
// Parallel agents get their own worktree off the parent's branch so
// simultaneous edits don't fight over one checkout. Without git there is
// no isolation to offer: the child shares the parent's workspace.
const worktreeBaseBranch = isGitRepo ? activeThreadBranch : null;

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.

🟠 High components/ChatView.tsx:4903

/parallel runs the child in activeThread.worktreePath when isGitRepo is true but activeThreadBranch is null, so parent and child edits share a checkout instead of being isolated. Reject this case (as the ordinary send path does) before starting the child, or otherwise require a valid base branch for worktree creation.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/ChatView.tsx around line 4903:

`/parallel` runs the child in `activeThread.worktreePath` when `isGitRepo` is true but `activeThreadBranch` is `null`, so parent and child edits share a checkout instead of being isolated. Reject this case (as the ordinary send path does) before starting the child, or otherwise require a valid base branch for worktree creation.

if (parallelAgentPrompt !== null) {
const hasNonTextContent =
composerImages.length > 0 ||
sendableComposerTerminalContexts.length > 0 ||

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.

🟡 Medium components/ChatView.tsx:5113

An expired terminal-context chip is ignored by the /parallel guard, so a successful spawn calls clearComposerDraftContent and silently deletes that context without the expired-context warning. Check composerTerminalContexts rather than only sendableComposerTerminalContexts so /parallel remains text-only and preserves contexts until explicitly removed.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/ChatView.tsx around line 5113:

An expired terminal-context chip is ignored by the `/parallel` guard, so a successful spawn calls `clearComposerDraftContent` and silently deletes that context without the expired-context warning. Check `composerTerminalContexts` rather than only `sendableComposerTerminalContexts` so `/parallel` remains text-only and preserves contexts until explicitly removed.

if (parentThread === undefined || parentThread.deletedAt !== null) {
return null;
}
let resultText: string | null = null;

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.

🟡 Medium orchestration/decider.ts:216

The parent completion activity can omit or truncate detail even when the child produced assistant text. parallelAgentFinishedActivityEvent reads thread.messages from the pre-thread.session.set read model, but ProviderRuntimeIngestion dispatches that session update before finalizeAssistantMessage flushes buffered text; the later message update does not refresh the activity. Generate this activity after assistant finalization or source the finalized buffer.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/orchestration/decider.ts around line 216:

The parent completion activity can omit or truncate `detail` even when the child produced assistant text. `parallelAgentFinishedActivityEvent` reads `thread.messages` from the pre-`thread.session.set` read model, but `ProviderRuntimeIngestion` dispatches that session update before `finalizeAssistantMessage` flushes buffered text; the later message update does not refresh the activity. Generate this activity after assistant finalization or source the finalized buffer.

Comment thread apps/server/src/ws.ts
interactionMode: bootstrap.createThread.interactionMode,
branch: bootstrap.createThread.branch,
worktreePath: bootstrap.createThread.worktreePath,
parentThreadId: bootstrap.createThread.parentThreadId ?? null,

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.

🟡 Medium src/ws.ts:908

The early thread.create dispatch records parallel-agent.started on the parent before worktree preparation, setup, and the final turn start succeed. If a later bootstrap step fails, cleanupCreatedThread deletes only the child, leaving the parent permanently reporting an agent that never started; defer parentThreadId propagation until bootstrap succeeds.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/ws.ts around line 908:

The early `thread.create` dispatch records `parallel-agent.started` on the parent before worktree preparation, setup, and the final turn start succeed. If a later bootstrap step fails, `cleanupCreatedThread` deletes only the child, leaving the parent permanently reporting an agent that never started; defer `parentThreadId` propagation until bootstrap succeeds.

// Parallel agents get their own worktree off the parent's branch so
// simultaneous edits don't fight over one checkout. Without git there is
// no isolation to offer: the child shares the parent's workspace.
const worktreeBaseBranch = isGitRepo ? activeThreadBranch : null;

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.

🟡 Medium components/ChatView.tsx:4903

/parallel creates the child from activeThreadBranch even when the local checkout has been switched externally, so the agent runs against the stale recorded branch instead of localCheckoutBranchMismatch.currentBranch. Reconcile the base branch used for both createThread.branch and prepareWorktree.baseBranch with the current checkout, as the normal send path does.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/ChatView.tsx around line 4903:

`/parallel` creates the child from `activeThreadBranch` even when the local checkout has been switched externally, so the agent runs against the stale recorded branch instead of `localCheckoutBranchMismatch.currentBranch`. Reconcile the base branch used for both `createThread.branch` and `prepareWorktree.baseBranch` with the current checkout, as the normal send path does.

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant