Skip to content

feat(TaskScheduler): introduce semaphore-based task scheduler (Epic 3, Story 3.1) - #1031

Merged
navedmerchant merged 9 commits into
mainfrom
issue/368
Jul 29, 2026
Merged

feat(TaskScheduler): introduce semaphore-based task scheduler (Epic 3, Story 3.1)#1031
navedmerchant merged 9 commits into
mainfrom
issue/368

Conversation

@edelauna

@edelauna edelauna commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Related GitHub Issue

Closes: #358

Description

Introduces TaskScheduler, a semaphore-based concurrency gate that wraps the existing TaskSemaphore utility, and wires it into ClineProvider as the single launch path for all tasks.

Key changes:

  • TaskScheduler (src/core/task/TaskScheduler.ts): thin wrapper around TaskSemaphore; schedule(task, run) acquires a permit, checks task.abort || task.abandoned before executing, and always releases in finally. Ships at maxConcurrency=1 (identical to current serial behaviour — raising it later enables fan-out without touching gate logic). cancelQueued() is called from ClineProvider.dispose() to drain the semaphore on teardown.

  • Task.run() (src/core/task/Task.ts): awaitable twin of Task.start(); returns the in-flight _runPromise if already running, or a resolved promise if _started is set (guards against double-invocation from constructor, start(), or a second run() call). ClineProvider now calls task.run() through the scheduler instead of task.start() directly.

  • ClineProvider (src/core/webview/ClineProvider.ts): holds a TaskScheduler instance; all task launch sites (createTask, delegateParentAndOpenChild) use void this.taskScheduler.schedule(task, () => task.run()). dispose() calls cancelQueued() before clearing the stack.

  • Tests: TaskScheduler.spec.ts (8 cases: immediate run, serial queuing, parallel at maxConcurrency=2, permit release on throw, cancelQueued, abort guard, abandoned guard, default concurrency); Task.dispose.test.ts gains a Task.run() idempotency describe (3 cases); provider-delegation.spec.ts and single-open-invariant.spec.ts updated to carry taskScheduler on provider stubs.

  • AGENTS.md: adds an ESLint Suppressions section documenting that counts must never increase, when as any is acceptable, and the prune-suppressions maintenance command. eslint-suppressions.json count for Task.dispose.test.ts reduced from 8 → 3.

No behaviour change at maxConcurrency=1 — tasks still run serially. The scheduler is the expand step of an expand-contract migration; fan-out (Story 3.2b) raises the limit without changing this gate logic.

Test Procedure

# Unit tests
pnpm --filter=zoo-code test -- --run core/task/__tests__/TaskScheduler.spec.ts
pnpm --filter=zoo-code test -- --run core/task/__tests__/Task.dispose.test.ts
pnpm --filter=zoo-code test -- --run __tests__/provider-delegation.spec.ts
pnpm --filter=zoo-code test -- --run __tests__/single-open-invariant.spec.ts

# Types + lint
pnpm --filter=zoo-code check-types
pnpm --dir src exec eslint --max-warnings=0 core/task/TaskScheduler.ts core/task/Task.ts core/webview/ClineProvider.ts

Pre-Submission Checklist

  • Issue Linked: Closes [Epic 3] TaskScheduler + Subtask Fan-out #358
  • Scope: TaskScheduler + Task.run() only; no fan-out logic yet (Story 3.2b is next)
  • Self-Review: Done
  • Testing: Unit tests added for scheduler, idempotency, and provider integration
  • Visual Snapshot: N/A — no UI changes
  • Documentation Impact: AGENTS.md updated with ESLint suppression guidance
  • Contribution Guidelines: Read and agreed

Summary by CodeRabbit

  • New Features
    • Added a task scheduler to control task start timing with configurable concurrency.
    • Added promise-based, idempotent run() execution for tasks.
  • Bug Fixes
    • Improved delegation sequencing/rollback when tasks execute via the scheduler.
    • Better abort handling during wait/resume flows.
    • Provider disposal now cancels queued (waiting) tasks to prevent post-teardown execution.
  • Tests
    • Expanded unit, integration, and end-to-end regression coverage for scheduler and run() behavior.
  • Documentation
    • Updated ESLint suppression guidance in AGENTS.md and adjusted suppression counts.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds idempotent task execution, a semaphore-backed TaskScheduler, and provider integration for scheduled startup and shutdown cancellation. Tests cover concurrency, ordering, rollback, and history-resume behavior.

Changes

Task scheduling

Layer / File(s) Summary
Promise-based task execution
src/core/task/Task.ts, src/core/task/__tests__/Task.dispose.test.ts, src/__tests__/task-run-dispatch.spec.ts, AGENTS.md, src/eslint-suppressions.json
Task.run() tracks and reuses its execution promise, handles task dispatch and abort-aware waits, and updates completed-task resume selection. Supporting tests and lint guidance are updated.
Concurrency scheduler
src/core/task/TaskScheduler.ts, src/core/task/__tests__/TaskScheduler.spec.ts
TaskScheduler limits concurrency, skips aborted or abandoned tasks, releases permits on completion or failure, and cancels queued work.
Provider scheduling integration
src/core/webview/ClineProvider.ts
Task creation and delegated child execution use TaskScheduler.schedule, while provider disposal cancels queued tasks.
Provider and resume flow validation
src/__tests__/provider-delegation.spec.ts, src/__tests__/single-open-invariant.spec.ts, apps/vscode-e2e/src/fixtures/subtasks.ts, apps/vscode-e2e/src/suite/subtasks.test.ts
Tests and fixtures cover scheduled run() calls, microtask timing, execution ordering, rollback behavior, and completed or interrupted task resumption.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related issues

Possibly related PRs

Suggested labels: awaiting-review

Suggested reviewers: taltas

Sequence Diagram(s)

sequenceDiagram
  participant ClineProvider
  participant TaskScheduler
  participant Task
  ClineProvider->>TaskScheduler: schedule(task, task.run)
  TaskScheduler->>TaskScheduler: acquire semaphore permit
  TaskScheduler->>Task: run()
  Task-->>TaskScheduler: resolve or reject
  TaskScheduler->>TaskScheduler: release concurrency permit
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing a semaphore-based TaskScheduler.
Description check ✅ Passed The description includes the linked issue, implementation summary, test procedure, and checklist with enough detail.
Linked Issues check ✅ Passed The changes implement Story 3.1 as described: a maxConcurrency=1 TaskScheduler integrated into task launch paths.
Out of Scope Changes check ✅ Passed No obvious unrelated code changes stand out; the docs and suppression updates support the scheduler work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue/368

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.

@edelauna edelauna changed the title Issue/368 feat(TaskScheduler): introduce semaphore-based task scheduler (Epic 3, Story 3.1) Jul 26, 2026
@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.57143% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/task/Task.ts 84.61% 1 Missing and 1 partial ⚠️
src/core/webview/ClineProvider.ts 81.81% 0 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/webview/ClineProvider.ts`:
- Around line 3191-3199: Update createTaskWithHistoryItem so its implicit task
startup uses taskScheduler.schedule(task, () => task.run()) instead of invoking
task.run directly, preserving the startTask: false opt-out and matching
createTask’s concurrency-gated startup behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9116becd-95dc-49c3-a4a2-4822b1a6ac95

📥 Commits

Reviewing files that changed from the base of the PR and between 2db2af0 and 7fc3e4b.

📒 Files selected for processing (9)
  • AGENTS.md
  • src/__tests__/provider-delegation.spec.ts
  • src/__tests__/single-open-invariant.spec.ts
  • src/core/task/Task.ts
  • src/core/task/TaskScheduler.ts
  • src/core/task/__tests__/Task.dispose.test.ts
  • src/core/task/__tests__/TaskScheduler.spec.ts
  • src/core/webview/ClineProvider.ts
  • src/eslint-suppressions.json

Comment thread src/core/webview/ClineProvider.ts
@edelauna
edelauna marked this pull request as ready for review July 28, 2026 21:49
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 28, 2026

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/task/Task.ts (1)

1886-1909: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Return a completion-bound promise when run() is called after auto-start.

TaskScheduler.schedule(..., () => task.run()) awaits its returned promise to gate concurrency, but for tasks with startTask: true, run() returns Promise.resolve() immediately because _started is set without assigning _runPromise. Add a completion promise/path there so scheduled tasks cannot let the gate release before the task actually completes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/task/Task.ts` around lines 1886 - 1909, Update Task.run() so the
_started branch returns a promise bound to the already auto-started task’s
actual completion instead of Promise.resolve(). Ensure the auto-start path
assigns or exposes _runPromise consistently with the normal run flow, while
preserving idempotent returns for subsequent calls and allowing TaskScheduler to
await completion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/core/task/Task.ts`:
- Around line 1886-1909: Update Task.run() so the _started branch returns a
promise bound to the already auto-started task’s actual completion instead of
Promise.resolve(). Ensure the auto-start path assigns or exposes _runPromise
consistently with the normal run flow, while preserving idempotent returns for
subsequent calls and allowing TaskScheduler to await completion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f606e03-9e62-4dcb-ad63-8eb02e1905d2

📥 Commits

Reviewing files that changed from the base of the PR and between dd37879 and 418802e.

📒 Files selected for processing (6)
  • apps/vscode-e2e/src/fixtures/subtasks.ts
  • apps/vscode-e2e/src/suite/subtasks.test.ts
  • src/__tests__/single-open-invariant.spec.ts
  • src/__tests__/task-run-dispatch.spec.ts
  • src/core/task/Task.ts
  • src/core/webview/ClineProvider.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/webview/ClineProvider.ts

Comment thread src/core/webview/ClineProvider.ts Outdated
@github-actions github-actions Bot removed the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 29, 2026
navedmerchant
navedmerchant previously approved these changes Jul 29, 2026
@navedmerchant
navedmerchant enabled auto-merge July 29, 2026 01:38
@navedmerchant
navedmerchant added this pull request to the merge queue Jul 29, 2026
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Jul 29, 2026
Merged via the queue into main with commit 1ae8b5b Jul 29, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Epic 3] TaskScheduler + Subtask Fan-out

2 participants