[rushd][WS2] Daemon host, routing & scheduling

Open
#5,897 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Quiet
Tech stack
node.js, typescript

Research direction

Start by reading the prerequisite issues #5895 and #5896, then trace the version-selected rush-lib entrypoint and the proposed @rushstack/rush-daemon serve command. The payload names no files or test paths; done means satisfying the listener, warm-session, routing, scheduling, I/O, parity, merging, opt-in, and CI acceptance criteria.

Written by the indexing model from the issue text.

Description

area:rushd

Build the long-lived per-workspace daemon: a warm WorkspaceSession (config + all-projects graph + watcher + plugins) that routes phased and global commands with per-request cwd/env/terminal context, matches in-process exit-code / warnings-as-errors semantics, and forwards stdin/raw-mode for interactive commands. It also serializes and merges concurrent client requests via exclusivity classes, queue-and-wait admission, and shared-build merging instead of failing on 'another rush is running'.

Depends on: #5895; #5896

Scope

  • Scaffold @rushstack/rush-daemon + serve. Launched by the version-selected rush-lib; boots a transport listener and signals readiness.
  • Warm WorkspaceSession. A warm RushConfiguration + all-projects OperationGraph + plugins + inputs snapshot + a long-lived ProjectWatcher that keeps invalidating operations even with no client attached.
  • Phased request routing. Map the selection via setEnabledStates, reconcile pending watcher invalidations, subscribe the client to its operations' raw streams + events, schedule/execute one iteration, and translate final status → exit code.
  • Per-request global-command context. Run global CLI actions with the client's cwd/env/terminal injected per request (never via global process.chdir or shared process.env mutation).
  • Exit-code parity. Match in-process exactly, including the SuccessWithWarning path (stderr-with-exit-0 → warnings, honoring allowWarningsInSuccessfulBuild/RUSH_ALLOW_WARNINGS_IN_SUCCESSFUL_BUILD).
  • Interactive I/O. Forward stdin bytes + raw-mode/resize control frames; classify PTY-only commands never-daemonize (run in-process on the client).
  • RequestScheduler. Exclusivity classes SHARED-BUILD/SHARED-READ/EXCLUSIVE + a static command-classification table; queue-and-wait admission (FIFO, an EXCLUSIVE "gate", queued-position progress events, clean Ctrl+C, --wait-timeout/--no-wait).
  • Shared-build merging. Union concurrent clients' enabled sets into one iteration, while each client's exit code reflects only its own subset.

Acceptance criteria

  • serve starts a listener at the workspace transport path and signals readiness; a ping control frame returns a pong (carrying protocol + daemon version); the entrypoint is launched via the version-selected rush-lib.
  • On start the session loads config once and builds the all-projects graph + plugins + inputs snapshot; a file change with no client connected marks the correct operations dirty (headless watcher); config/graph are reused across requests (no rebuild).
  • A rush build --to X request enables X's subtree, runs one iteration, and returns the same result set as in-process; a no-op follow-up collapses to a warm skip; the client receives only its selected operations' streams/events and the correct exit code.
  • A global command observes the client's cwd/env while the daemon's own process.cwd()/process.env stay unchanged; two concurrent global commands from different cwds/envs do not cross-contaminate.
  • Success, SuccessWithWarning, and failure cases each return the same exit code as in-process, including under the warnings-as-errors settings/env (table-driven parity test).
  • stdin bytes and raw-mode toggles forward correctly (an interactive prompt behaves identically to in-process); PTY-only commands are on the never-daemonize list and transparently run in-process.
  • Every built-in command has an exclusivity class (unclassified/global default EXCLUSIVE); the scheduler admits compatible classes concurrently and serializes incompatible ones; an EXCLUSIVE request gates later admissions until it finishes; --wait-timeout expires with the documented exit code and --no-wait fails fast.
  • Two overlapping SHARED-BUILD selections run as one merged iteration (shared operations execute once), a failure in one client's subset does not fail another's succeeding subset, and each client gets only its subset's streams/events and a correct exit code.
  • All new daemon behavior ships opt-in until cutover; unit/integration tests green in CI.

Part of #5894.

Dominant language
TypeScript
Stars
6.5k
Forks
708
Avg merge
5d 7h
Merged PRs (30d)
44

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/rushstack

All issues in microsoft/rushstack

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.