Skip to content

[rush] (BREAKING CHANGE) Overhaul watch-mode to facilitate orchestration#5378

Open
dmichon-msft wants to merge 21 commits intomicrosoft:mainfrom
dmichon-msft:watch-rework
Open

[rush] (BREAKING CHANGE) Overhaul watch-mode to facilitate orchestration#5378
dmichon-msft wants to merge 21 commits intomicrosoft:mainfrom
dmichon-msft:watch-rework

Conversation

@dmichon-msft
Copy link
Contributor

@dmichon-msft dmichon-msft commented Sep 27, 2025

[rush] (BREAKING CHANGE) Overhaul watch-mode to facilitate orchestration

Summary

Completely retools the watch engine in Rush to facilitate better interaction with plugins that wish to orchestrate the build process. Makes the Rush execution engine stateful across an entire Rush watch session.

BREAKING CHANGES

  • PhasedCommandHooks now has only two hooks: createOperationsAsync and onGraphCreatedAsync. All other hooks have been moved to OperationGraphHooks, accessible via operationGraph.hooks inside the onGraphCreatedAsync callback.
  • createOperations is now createOperationsAsync, and the properties on the ICreateOperationsContext parameter have changed: isInitial, projectsInUnknownState, phaseOriginal, and invalidateOperation have been removed; generateFullGraph and includePhaseDeps have been added.
  • All manipulation of the runtime graph and taps into the build process are now in the OperationGraphHooks class. Tap operationGraph.hooks for:
    • configureIteration — synchronous hook to select which operations run in the next iteration
    • beforeExecuteIterationAsync — replaces beforeExecuteOperationsAsync
    • afterExecuteIterationAsync — replaces afterExecuteOperationsAsync
    • beforeExecuteOperationAsync — moved from PhasedCommandHooks
    • afterExecuteOperationAsync — moved from PhasedCommandHooks
    • createEnvironmentForOperation — moved from PhasedCommandHooks
    • beforeLog — moved from PhasedCommandHooks; now invoked by the graph before writing telemetry
    • onIdle — replaces waitingForChanges (moved from PhasedCommandHooks)
    • onExecutionStatesUpdated, onEnableStatesChanged, onIterationScheduled, onGraphStateChanged, onInvalidateOperations — new hooks
  • IOperationRunnerContext now includes getInvalidateCallback(), which returns a lightweight (reason: string) => void callback that marks the current operation for re-execution. This replaces the previous invalidateOperation on ICreateOperationsContext and removes the need for runners to capture an IOperationGraph reference.
  • IOperationRunner.executeAsync now takes an optional second parameter lastState?: IOperationLastState, providing the previous execution result to inform incremental behavior (e.g. choosing an initial vs. incremental command).
  • IBaseOperationExecutionResult.getStateHashComponents() now returns a structured IOperationStateHashComponents interface ({ dependencies, local, config }) instead of a flat ReadonlyArray<string>.
  • IBaseOperationExecutionResult.metadataFolderPath is now string (was string | undefined).

Details

The new lifecycle of a Rush phased command is that the command first invokes createOperationsAsync to create the session-long operation graph. This set of operations is then passed into onGraphCreatedAsync, which constructs an IOperationGraph that owns the lifecycle of the execution session.

There is a new watch option includeAllProjectsInWatchGraph (in command-line.json) that, if set to true, will cause Rush to build the graph with all projects in rush.json, regardless of CLI selection parameters. Selected projects will only affect which projects are enabled for execution during the initial run. This also allows for a bare command, e.g. rush start, to select no projects. This feature is intended for use with plugins that offer the ability to alter the enabled/disabled states of operations in the graph while the session is ongoing. For an example, see @rushstack/rush-serve-plugin, which facilitates altering these states via Web Socket messages.

Runner self-invalidation

Long-lived runners (e.g. IPC processes, file watchers) can now request re-execution directly via context.getInvalidateCallback(). This returns a minimal closure that delegates to IOperationGraph.invalidateOperations(), without the runner needing access to the graph or Operation object. The IPCOperationRunner uses this internally to handle requestRun IPC messages from child processes.

Error handling

Operation runner errors are now uniformly caught and wrapped in OperationError by OperationExecutionRecord.executeAsync, rather than requiring each runner implementation to handle its own error wrapping.

All in-repo plugins that interact with the Rush execution graph have also been updated.

How it was tested

Added unit tests for all functionality of the new IOperationGraph API contract (633 tests passing).
Manual validation via the rushstack repo's rush start command for the CLI interaction (enable/disable debug or verbose, alter parallelism, pause/resume, kick a single build, invalidate, close runners).

Impacted documentation

All watch-mode documentation. Plugin documentation for phased commands.

New docs added in this PR:

  • docs/rush/phased-commands.md — architecture reference for the phased command execution model, OperationGraphHooks, IOperationGraph API, and IOperationRunner contract
  • docs/rush/plugin-migration-guide.md — hook-by-hook migration guide for plugin authors upgrading from the previous API

@dmichon-msft dmichon-msft force-pushed the watch-rework branch 2 times, most recently from 284dc4c to 2224f15 Compare March 16, 2026 21:11
* @alpha
*/
export interface IExecuteOperationsContext extends ICreateOperationsContext {
export interface IOperationExecutionManager {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should move somewhere else.

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

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants