Worktree engine: parallel tasks, live output, PR import, all comments#20
Merged
Conversation
Users now see what Claude is doing in real time during task execution. Previously, tasks ran opaquely for up to 30 minutes with only a static status banner, making it impossible to tell if Claude was stuck or working. A new dedicated Agent tab in the task detail view shows Claude's text responses, tool uses, and final results as they happen. Backend (Rust): - Switch invoke_claude_cli from --output-format json to stream-json with --verbose for newline-delimited JSON events in real time - Unified streaming implementation replaces dual stdin/no-stdin paths - Parse each JSON event, extract structured content blocks (text, tool_use with target path, tool_result, thinking, result) - Emit agent:task-output Tauri events for the frontend per event - Synthesize a CliResult compatible with existing parse_implement_output and parse_review_output so worker logic is unchanged - Thread AppHandle and task_id from engine_start_task and engine_address_review through worker to invoke_claude_cli Frontend: - TaskOutputEvent type with structured ContentBlock[] (camelCase via serde rename_all) - useTaskOutputStream hook with 500-line buffer, DB persistence, historical event loading on mount - TaskLiveOutput component: terminal-like log with per-block rendering, icons for tool use, auto-scroll with jump-to-bottom - Structured result cards for review/implement JSON output (passed status, issues by severity, files modified) - Syntax highlighting via highlight.js for fenced code blocks and diff output from tool_result - Agent tab in TaskDetailView auto-selects when task transitions to in_progress, persists after completion via SQLite Persistence: - Migration 19 adds task_agent_events table (task_id, event_type, blocks_json, timestamp) - Events saved async as they arrive via saveAgentEvent - Historical events loaded on mount via listAgentEvents
Enable multiple tasks to run simultaneously now that the worktree migration makes each task filesystem-isolated. Default concurrency is 5, configurable in Settings → General. Scans count against the same pool so SUSTN never over-subscribes Claude CLI. Backend (Rust): - EngineState.running_tasks is now Mutex<HashMap<String, CurrentTask>> instead of Mutex<Option<CurrentTask>>, tracking multiple tasks by id - New concurrency_limit (RwLock<usize>, default 5), tokens_reserved (Mutex<i64>), active_scans (Mutex<usize>) on EngineState - engine_start_task and engine_address_review: capacity check sums running_tasks + active_scans; register/release on task boundaries; reserve/release estimated tokens via budget::estimated_task_tokens - engine_scan_now: increments active_scans for pass 1 and spawned pass 2, releases on both success and error paths - budget::calculate_budget_status_with_reservation subtracts reserved tokens so concurrent starts do not over-commit the daily budget - New engine_set_concurrency_limit Tauri command (clamped 1..=10) - EngineStatusResponse exposes running_tasks and concurrency_limit Frontend: - useQueueProcessor: removed single-task processingRef, now checks runningTasks.length + concurrencyLimit via engine_get_status before dequeuing. Chains processNext on task completion or enqueue - useScheduler: skips tick when at concurrency capacity instead of when any task is running - TaskDetailView: queues only when at capacity, starts immediately when a slot is available - TaskStatusBanner: checks runningTasks.some(t => t.taskId === id) for per-task working state - Deep scan listener invalidates every running task in the affected repo - useStartupRecovery syncs persisted concurrencyLimit to the Rust state at boot so the setting survives restarts Scan scope control: - Migration 21: scan_enabled column on agent_config (default 1) - AgentConfig.scanEnabled wired through DB layer and useUpdateAgentConfig - Scheduler and startup scan skip repos with scanEnabled=false - pr-import sets scanEnabled=false on imported repos; they never auto-scan again unless the user explicitly re-enables in project settings (new toggle in the Automation section) PR lifecycle parallelism: - prLifecycleTick now processes active PRs via Promise.allSettled instead of a sequential for-await loop, so multiple PRs can sync and address in parallel (bounded by backend concurrency limit) Settings: - GlobalSettings.concurrencyLimit (default 5), persisted in global_settings via migration 20 - General settings section: parallel-tasks selector (1-10) - useUpdateGlobalSetting propagates the limit to the Rust state on change so it takes effect immediately without restart
Previously the PR lifecycle only fetched inline review comments
(pulls/{n}/comments). General issue-level PR comments and the free-text
body of review submissions were either ignored or stored but never
surfaced to Claude, so whole classes of reviewer feedback went
unanswered.
The pr_comments table now carries a `kind` column (inline | issue |
review_summary) with a composite unique index on (kind,
github_comment_id) since those IDs come from different server-side
tables and can collide. Migration 22 also stamps a
pr_comments_rollout_cutoff timestamp so existing active PRs don't get
retroactively reworked — any source comment older than the cutoff is
inserted as already-addressed.
The service now pulls all three sources in each tick:
- listPrComments → kind=inline (existing behavior)
- listIssueComments → kind=issue (new)
- synthesized per review with non-empty body → kind=review_summary
Bot-authored issue-comment replies carry a hidden `<!-- sustn:task=... -->`
marker so they're filtered out on the next fetch without needing to
resolve the gh user's login (robust against auth swaps).
Each item sent to Claude is tagged [KIND: ...] in addition to the
existing COMMENT_ID tag; the reply JSON requires `kind` echoed back so
we can route inline replies to the pull-comment replies endpoint and
issue / review-summary replies to a new top-level issue comment. The
resolved-threads filter is now scoped to kind=inline to avoid dropping
issue rows whose ID coincidentally equals a resolved inline thread's
root ID.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This branch lands the worktree-based engine rework plus several
user-visible upgrades that build on top of it.
Worktree engine migration. Each task now runs in its own git
worktree so concurrent agents never step on each other's working
trees or branches. Adds
src-tauri/src/engine/worktree.rsandthreads worktree paths through the engine + tasks table. Also adds
PR import so existing PRs can be adopted by an agent.
Live streaming of Claude CLI output. The Agent tab now renders
Claude's tool calls and assistant messages as they arrive instead
of waiting for the final summary. New
TaskLiveOutputcomponent,useTaskOutputStreamhook, and atask_agent_eventstable thatpersists the stream so it survives app restarts.
Parallel task execution with scan-aware concurrency. Multiple
tasks can run in parallel up to the configured concurrency limit,
with scans counted against the same budget so we don't flood the
Claude CLI.
Full PR comment coverage. The watcher previously only addressed
inline review comments. It now also handles general issue-level PR
comments and the free-text body of review submissions.
pr_commentsgets a
kindcolumn (inline | issue | review_summary) with acomposite unique index, and a rollout cutoff so existing active
PRs aren't retroactively reworked. Bot replies carry a hidden
marker for fetch-time dedup.