Skip to content

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

Merged
bmiddha merged 27 commits into
microsoft:mainfrom
dmichon-msft:watch-rework
Jul 15, 2026
Merged

[rush] (BREAKING CHANGE) Overhaul watch-mode to facilitate orchestration#5378
bmiddha merged 27 commits into
microsoft:mainfrom
dmichon-msft:watch-rework

Conversation

@dmichon-msft

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

Copy link
Copy Markdown
Contributor

[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

Comment thread libraries/rush-lib/src/logic/operations/OperationExecutionManager.ts Outdated
Comment thread libraries/rush-lib/src/logic/operations/OperationGraph.ts
Comment thread libraries/rush-lib/src/logic/operations/OperationGraph.ts
Comment thread libraries/rush-lib/src/logic/operations/OperationGraph.ts
Comment thread libraries/rush-lib/src/logic/operations/IPCOperationRunnerPlugin.ts
Comment thread libraries/rush-lib/src/logic/operations/OperationExecutionManager.ts Outdated
Comment thread libraries/rush-lib/src/logic/operations/OperationGraph.ts Outdated
Comment thread libraries/rush-lib/src/logic/operations/OperationGraph.ts Outdated
@dmichon-msft dmichon-msft force-pushed the watch-rework branch 2 times, most recently from 284dc4c to 2224f15 Compare March 16, 2026 21:11
Comment thread common/config/rush-plugins/rush-serve-plugin.json
Comment thread libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts
Comment thread libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts Outdated
Comment thread libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts Outdated
Comment thread libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts Outdated
Comment thread libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts
Comment thread libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts Outdated
Comment thread libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts Outdated
Comment thread libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts
Comment thread libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts Outdated
Comment thread common/reviews/api/rush-lib.api.md
Comment thread libraries/rush-lib/src/logic/operations/OperationGraph.ts Outdated
@dmichon-msft dmichon-msft force-pushed the watch-rework branch 2 times, most recently from 4ba3025 to 289ac41 Compare March 20, 2026 23:38
@bmiddha bmiddha requested review from Copilot and removed request for D4N14L July 14, 2026 23:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new stateful OperationGraph execution model for Rush watch-mode/phased commands, moving per-iteration lifecycle hooks off PhasedCommandHooks and into a dedicated OperationGraphHooks exposed via operationGraph.hooks, to enable better orchestration by plugins across a watch session.

Changes:

  • Replace “recreate operations per pass” with a session-long IOperationGraph and per-iteration hooks (OperationGraphHooks).
  • Update in-repo plugins (serve, bridge-cache, buildxl graph) to the new createOperationsAsync + onGraphCreatedAsync lifecycle and new runner contracts.
  • Add/adjust tests and public API surface for new OperationGraph/parallelism/state-hash structures and options (e.g. include-all-projects watch graph).
Show a summary per file
File Description
rush-plugins/rush-serve-plugin/src/phasedCommandHandler.ts Migrates serve plugin to new phased command lifecycle; adds dependency expansion and graph idle hook.
rush-plugins/rush-serve-plugin/src/api.types.ts Updates WS API contracts for new graph/execution-state model.
rush-plugins/rush-serve-plugin/README.md Updates client example code to new WS message shapes and state handling.
rush-plugins/rush-serve-plugin/package.json Promotes @rushstack/terminal to dependencies.
rush-plugins/rush-buildxl-graph-plugin/src/test/GraphProcessor.test.ts Updates tests to new runner shape/config-hash access.
rush-plugins/rush-buildxl-graph-plugin/src/GraphProcessor.ts Switches graph “command” derivation to runner config hash.
rush-plugins/rush-buildxl-graph-plugin/src/DropBuildGraphPlugin.ts Updates hook usage to createOperationsAsync.
rush-plugins/rush-buildxl-graph-plugin/src/debugGraphFiltering.ts Adjusts debug filtering/return types for updated debug graph shape.
rush-plugins/rush-bridge-cache-plugin/src/BridgeCachePlugin.ts Reworks cache plugin to run via IOperationGraph iteration hooks.
libraries/rush-sdk/src/test/snapshots/script.test.ts.snap Updates SDK export snapshot for new symbols.
libraries/rush-lib/src/utilities/test/Stopwatch.test.ts Adds tests for new startTimeOverride behavior.
libraries/rush-lib/src/utilities/Stopwatch.ts Adds optional startTimeOverride support to start() and static start().
libraries/rush-lib/src/schemas/command-line.schema.json Adds schema for includeAllProjectsInWatchGraph.
libraries/rush-lib/src/pluginFramework/PhasedCommandHooks.ts Removes old hooks, adds createOperationsAsync and onGraphCreatedAsync + new contexts.
libraries/rush-lib/src/pluginFramework/OperationGraphHooks.ts New hook container for per-iteration and per-operation execution lifecycle.
libraries/rush-lib/src/logic/operations/WeightedOperationPlugin.ts Removes old weight plugin (weight handling moved into execution record).
libraries/rush-lib/src/logic/operations/ValidateOperationsPlugin.ts Moves validation to onGraphCreatedAsync operating on graph operations.
libraries/rush-lib/src/logic/operations/test/WeightedOperationPlugin.test.ts Removes tests for deleted weight plugin.
libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerPlugin.test.ts Updates tests to new hooks/context shape.
libraries/rush-lib/src/logic/operations/test/PhasedOperationPlugin.test.ts Updates tests for new graph lifecycle; adds full-graph enable/disable scenario.
libraries/rush-lib/src/logic/operations/test/ParseParallelism.test.ts Adds new unit tests for parse/coerce parallelism helpers.
libraries/rush-lib/src/logic/operations/test/OperationExecutionRecord.test.ts Adds tests for weight coercion and record behavior.
libraries/rush-lib/src/logic/operations/test/Operation.test.ts Adds tests for new operation weight parsing and validation.
libraries/rush-lib/src/logic/operations/test/IgnoredParametersPlugin.test.ts Updates tests to access env hook from graph.hooks.
libraries/rush-lib/src/logic/operations/test/BuildPlanPlugin.test.ts Updates tests to execute via OperationGraph.
libraries/rush-lib/src/logic/operations/test/AsyncOperationQueue.test.ts Updates test setup for new execution record context requirements.
libraries/rush-lib/src/logic/operations/test/snapshots/PhasedOperationPlugin.test.ts.snap Updates snapshots for new selection/config semantics.
libraries/rush-lib/src/logic/operations/test/snapshots/ParseParallelism.test.ts.snap Adds snapshots for new parseParallelism errors.
libraries/rush-lib/src/logic/operations/test/snapshots/OperationGraph.test.ts.snap Renames snapshots to OperationGraph naming.
libraries/rush-lib/src/logic/operations/ShellOperationRunnerPlugin.ts Updates runner initialization for initial/incremental commands.
libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts Updates runner signature + chooses initial vs incremental command per last state.
libraries/rush-lib/src/logic/operations/ShardedPhaseOperationPlugin.ts Updates shell runner initialization signature for shards.
libraries/rush-lib/src/logic/operations/PnpmSyncCopyOperationPlugin.ts Moves hook tap to graph.hooks.afterExecuteOperationAsync.
libraries/rush-lib/src/logic/operations/PhasedOperationPlugin.ts Splits graph construction vs per-iteration enablement logic into graph hook.
libraries/rush-lib/src/logic/operations/ParseParallelism.ts New parallelism parsing/coercion utilities and core-count caching.
libraries/rush-lib/src/logic/operations/OperationStatus.ts Adds SUCCESS_STATUSES helper set.
libraries/rush-lib/src/logic/operations/OperationResultSummarizerPlugin.ts Moves summarizer to afterExecuteIterationAsync.
libraries/rush-lib/src/logic/operations/OperationExecutionRecord.ts Adds per-iteration enabled, weight coercion, invalidation callback, state-hash structuring, and centralized error wrapping.
libraries/rush-lib/src/logic/operations/OperationExecutionManager.ts Deletes old stateless execution manager in favor of OperationGraph.
libraries/rush-lib/src/logic/operations/Operation.ts Adds OperationEnabledState and scalar weight parsing; updates docs.
libraries/rush-lib/src/logic/operations/NodeDiagnosticDirPlugin.ts Moves env mutation hook to graph.hooks.createEnvironmentForOperation.
libraries/rush-lib/src/logic/operations/LegacySkipPlugin.ts Migrates skip logic to graph iteration/operation hooks.
libraries/rush-lib/src/logic/operations/IPCOperationRunnerPlugin.ts Reworks IPC runner selection/commands for new incremental contract and invalidation callback.
libraries/rush-lib/src/logic/operations/IPCOperationRunner.ts Implements lastState-driven initial/incremental command selection; uses invalidate callback; renames shutdown to close.
libraries/rush-lib/src/logic/operations/IOperationRunner.ts Extends runner contract with lastState, invalidation callback, optional active/close.
libraries/rush-lib/src/logic/operations/IOperationGraph.ts New public graph API contract (iteration scheduling, invalidation, enabled states, etc.).
libraries/rush-lib/src/logic/operations/IOperationExecutionResult.ts Introduces structured state hash components and configurable operation/result interfaces.
libraries/rush-lib/src/logic/operations/IgnoredParametersPlugin.ts Moves env mutation to graph hook.
libraries/rush-lib/src/logic/operations/DebugHashesPlugin.ts Moves hash printing to graph iteration config hook and structured hash output.
libraries/rush-lib/src/logic/operations/ConsoleTimelinePlugin.ts Moves timeline printing to afterExecuteIterationAsync.
libraries/rush-lib/src/logic/operations/BuildPlanPlugin.ts Moves build plan generation to graph iteration config hook.
libraries/rush-lib/src/logic/buildCache/OperationBuildCache.ts Generalizes build cache API to accept IBaseOperationExecutionResult.
libraries/rush-lib/src/index.ts Exposes new graph/parallelism/state-hash types and hooks in public index.
libraries/rush-lib/src/cli/RushCommandLineParser.ts Threads new watch option includeAllProjectsInWatchGraph into action setup.
libraries/rush-lib/src/cli/parsing/test/ParseParallelism.test.ts Removes old CLI parsing parallelism tests (moved to operations layer).
libraries/rush-lib/src/cli/parsing/SelectionParameterSet.ts Allows empty selection for watch “full graph” behavior.
libraries/rush-lib/src/cli/parsing/ParseParallelism.ts Removes old parallelism parsing implementation (relocated).
libraries/rush-lib/src/api/EventHooks.ts Minor doc wording update.
libraries/rush-lib/src/api/CommandLineJson.ts Adds includeAllProjectsInWatchGraph to command-line JSON typing.
libraries/rush-lib/src/api/CommandLineConfiguration.ts Plumbs includeAllProjectsInWatchGraph through normalized config.
common/reviews/api/rush-lib.api.md Updates API report to include new graph/hooks/types and breaking changes.
common/config/subspaces/default/pnpm-lock.yaml Updates lockfile for added rush-serve-plugin/terminal linkage.
common/config/rush/nonbrowser-approved-packages.json Approves @rushstack/rush-serve-plugin for nonbrowser usage.
common/config/rush/experiments.json Enables useIPCScriptsInWatchMode.
common/config/rush/command-line.json Adjusts phase dependency declarations; adds --port for start.
common/config/rush-plugins/rush-serve-plugin.json Adds config file for enabling rush-serve-plugin for start.
common/changes/@microsoft/rush/watch-rework_2025-09-26-23-50.json Adds change file documenting breaking change.
apps/rush/src/start-dev.ts Includes rush-serve-plugin in dev start harness.
apps/rush/package.json Adds rush-serve-plugin dependency.

Review details

Files not reviewed (1)
  • common/config/subspaces/default/pnpm-lock.yaml: Generated file
  • Files reviewed: 78/80 changed files
  • Comments generated: 6
  • Review effort level: Low

Comment thread rush-plugins/rush-serve-plugin/src/api.types.ts
Comment thread rush-plugins/rush-serve-plugin/src/api.types.ts
Comment thread rush-plugins/rush-serve-plugin/src/api.types.ts
Comment thread libraries/rush-lib/src/logic/operations/ShellOperationRunner.ts Outdated
Comment thread libraries/rush-lib/src/logic/operations/Operation.ts
Comment thread rush-plugins/rush-bridge-cache-plugin/src/BridgeCachePlugin.ts
bmiddha and others added 4 commits July 15, 2026 18:26
Address PR review feedback on the rush-serve-plugin public API types:

- IOperationInfo.enabled doc listed an `always` state, but
  ReadableOperationEnabledState uses `affected`. Align the doc with the
  actual type value.
- IWebSocketTerminalChunkEventMessage is emitted by the server but was
  missing from the IWebSocketEventMessage union. Add it so the public
  event message type is complete for consumers.
- IWebSocketSyncOperationsEventMessage doc claimed a refresh of "dynamic
  execution states", but its payload is the static graph definition
  (operations: IOperationInfo[]). Correct the comment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 43593fac-d913-42fd-842a-f88f5031acf2
The "Invoking (...)" log line reported "incremental" whenever a
lastState was provided, even when no incremental command existed and the
initial command was actually run. Derive the label from the command that
is actually selected so logs and telemetry correlation are accurate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 43593fac-d913-42fd-842a-f88f5031acf2
The fallback context string used when an operation has no runner was
missing a closing parenthesis (and left the phase-name segment
unterminated), producing a confusing "Invalid weight for operation" error
message. Close the parenthesis so the operation identifier renders
correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 43593fac-d913-42fd-842a-f88f5031acf2
When a tap short-circuits beforeExecuteIterationAsync by returning an
OperationStatus, OperationGraph marked every not-yet-executed operation
as Aborted. For plugins that perform the work out-of-band and bail with a
successful status (e.g. rush-bridge-cache reading/writing the cache), this
misrepresented operations that were intentionally not executed.

Mark the remaining operations as Skipped when the bail status is a
successful one (Success, FromCache, NoOp); genuine aborts and failures
still mark remaining work as Aborted. Update the hook documentation and
the bridge-cache comment accordingly, and add a regression test covering
a successful bail.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 43593fac-d913-42fd-842a-f88f5031acf2
@bmiddha bmiddha enabled auto-merge (squash) July 15, 2026 18:55
@bmiddha bmiddha merged commit aceb1fe into microsoft:main Jul 15, 2026
6 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Closed in Bug Triage Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Closed

Development

Successfully merging this pull request may close these issues.

4 participants