Skip to content

feat(tasks): run tasks across several workspaces - #374

Open
arcanis wants to merge 2 commits into
mael/tasks-root-defaultsfrom
mael/tasks-workspace-selection
Open

arcanis wants to merge 2 commits into
mael/tasks-root-defaultsfrom
mael/tasks-workspace-selection

Conversation

@arcanis

@arcanis arcanis commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Issue: yarn tasks run could only run a task in the current workspace.

Fix: new workspace selection options:

  • -A, --from <glob>, --affected / --since <ref>;
  • --with-dependencies / --dependencies-only / --with-dependents;
  • --include / --exclude / --only;
  • -j, --continue, --errors-only, and comma-separated task names.

All targets are resolved into one deduplicated graph. Concurrency is per context, and no-op tasks don't take a slot. Multi-workspace runs are fail-fast by default. --standalone now defaults to on in CI, so no daemon outlives the job.

Covered by acceptance tests. Stacked on #373.


Note

High Risk
Touches daemon scheduling, IPC, and shared task-graph resolution—bugs could mis-order runs, leak wrong dependencies across contexts, or change exit/cancel behavior in CI and monorepo workflows.

Overview
yarn tasks run is consolidated into one command (replacing separate interlaced/buffered/silent-deps entry points) and can run the same task(s) across many workspaces with turbo-style selection: -A, --from, --affected / --since, dependency/dependent graph flags, --include/--exclude, and --only to drop cross-workspace ^ deps outside the selection. Comma-separated task names resolve into a single deduplicated graph via new resolve_many / resolve_tasks; the daemon batches multi-target pushes, honors per-context -j concurrency (no-op tasks don’t consume slots), and keeps --only prerequisite edges scoped per context so concurrent runs don’t corrupt the shared graph.

Multi-workspace runs are fail-fast by default (first failure cancels the context; --continue opts out). New --errors-only logging, default --standalone on CI, and TaskSubscription.workspace for per-workspace targets. yarn run <task> still falls through to silent-deps via a shared helper.

Reviewed by Cursor Bugbot for commit a2b4482. Bugbot is set up for automated code reviews on this repo. Configure here.

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

Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 3 issues found in the latest run.

  • ✅ Fixed: Batch resolve overwrites shared task graph
    • Changed insert() to entry().or_insert() in add_tasks_batch to prevent concurrent contexts from overwriting shared prerequisites.
  • ✅ Fixed: Fail-fast drops dependency exit codes
    • Added exit code recording before fail-fast cancellation to preserve the original failure code from dependencies.
  • ✅ Fixed: --only skipped for long-lived tasks
    • Added only parameter to add_task method to apply workspace filtering consistently for long-lived tasks.

Create PR

You can send follow-ups to the cloud agent here.

Comment thread packages/zpm/src/daemon/coordinator_state/task_graph.rs
Comment thread packages/zpm/src/commands/tasks/runner.rs
Comment thread packages/zpm/src/daemon/coordinator.rs
`yarn tasks run` only ran a task in the current workspace.

It now accepts workspace selection options:
- `-A,--all`, and `--from <glob>` (idents or paths, repeatable);
- `--affected` / `--since <ref>` (changed workspaces plus their
  dependents);
- `--with-dependencies` (alias `--recursive`), `--dependencies-only`,
  `--with-dependents`;
- `--include` / `--exclude` to filter the final selection, and
  `--only` to drop `^` dependencies outside of it;
- `-j,--concurrency`, `--continue`, `--errors-only`, and
  comma-separated task names (`build,typecheck`).

All targets are resolved into a single deduplicated graph. Concurrency
is enforced per context, and script-less tasks don't take a slot.
Multi-workspace runs stop at the first failure unless `--continue` is
set.

`--standalone` now defaults to on in CI, so no background daemon
outlives the job.
- --only graphs were written into the dependency map every context
  shares. A full run overlapping an --only run on the same daemon could
  then start a task before its real dependencies. --only edges now live
  in a per-context override.
- --only didn't apply to long-lived targets, which don't go through the
  batch, so their ^ dependencies still ran.
- When a dependency (rather than a target) failed first, fail-fast
  reported 1 for the cancelled targets instead of its exit code.
@arcanis
arcanis force-pushed the mael/tasks-workspace-selection branch from 63f285a to a2b4482 Compare October 9, 2026 13:59

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

Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix is ON, but it could not run because the spend limit has been reached. To enable Bugbot Autofix, raise your spend limit in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a2b4482. Configure here.

self.context_prerequisites
.entry(ctx_id.clone())
.or_default()
.insert(tid, prereqs);

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.

Only-flag pollutes long-lived prerequisites

High Severity

--only writes filtered prerequisites into context_prerequisites keyed by the task's context. Long-lived targets run under the shared LONG_LIVED_CONTEXT_ID, so those stripped edges are reused by later long-lived runs in the same daemon. A follow-up run without --only (or a restart while another long-lived task keeps that context alive) can skip ^ dependencies that should still run.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a2b4482. Configure here.

.collect();

let workspace_filter: Option<BTreeSet<Ident>>
= only.then(|| root_tasks.iter().map(|task_id| task_id.workspace.clone()).collect());

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.

Only-filter omits long-lived workspaces

Medium Severity

--only batch resolution builds its workspace filter from batched (non-long-lived) targets only. Long-lived targets are excluded from the batch, so a mixed run can drop in-selection ^ dependencies that point at those long-lived workspaces even though they were part of the original selection.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a2b4482. Configure here.

}

map
}

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.

Dependents graphs disagree on peer deps

Medium Severity

--affected / --since walk dependents through workspaces_with_dependents, which includes peer dependencies. --with-dependents inverts workspace_dependency_map, which only follows hard dependencies. A workspace that peers on a changed package is selected by --affected but missed by --from pkg --with-dependents.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a2b4482. Configure here.

This branch has not been deployed

No deployments
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