Repository navigation
[rush-daemon] Don't restart the daemon for volatile per-shell environment variables - #6062
Merged
Sean Larkin (TheLarkInn) merged 4 commits intoSep 24, 2026
Conversation
…rints Fixes #6047 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Mo Jazayeri (mojaza)
approved these changes
Sep 24, 2026
Sean Larkin (TheLarkInn)
deleted the
thelarkinn-fix-rushd-env-fingerprint-restarts
branch
September 24, 2026 18:00
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unconditional exclusions can invalidate dependsOnEnvVars semantics and preserve stale WSL or SSH session handles.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
What changed in this PR
Centralizes daemon environment normalization to avoid restarts caused by volatile shell variables.
Changes:
- Adds shared environment-filtering APIs.
- Applies normalization to fingerprints and daemon checks.
- Adds tests, documentation, API reports, and change records.
| File | Description |
|---|---|
libraries/rush-sdk/src/test/__snapshots__/script.test.ts.snap |
Records new exports. |
libraries/rush-lib/src/index.ts |
Exports normalization APIs. |
libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts |
Filters environment fingerprints. |
libraries/rush-lib/src/api/test/WorkspaceInputFingerprint.test.ts |
Tests filtering behavior. |
libraries/rush-daemon/src/test/WorkspaceReloadTierStatus.test.ts |
Tests warm daemon reuse. |
libraries/rush-daemon/src/ProductionDaemonRequestResolver.ts |
Shares normalized comparison. |
libraries/rush-daemon/README.md |
Documents environment semantics. |
common/reviews/api/rush-lib.api.md |
Updates rush-lib API report. |
common/reviews/api/rush-daemon.api.md |
Updates daemon API report. |
common/changes/@rushstack/rush-daemon/env-fingerprint_2026-09-23.json |
Adds daemon change record. |
common/changes/@microsoft/rush/env-fingerprint_2026-09-23.json |
Adds Rush change record. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| * | ||
| * @alpha | ||
| */ | ||
| export const workspaceFingerprintIgnoredEnvironmentVariables: ReadonlySet<string> = new Set([ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
The Rush daemon restarted its whole process (tier 2) whenever a request's environment differed from the daemon's startup environment in any variable. Per-shell variables such as
PWD(aftercd),OLDPWD,SHLVL,_,TERM,COLUMNSandWSL_INTEROP(new for everywsl.exe) each caused a restart. The warm graph was lost and alternating shells thrashed the daemon. This PR excludes a centrally defined, documented set of volatile variables from environment fingerprints. Variables that can affect the engine, the graph or operation hashes still trigger a restart.Root cause
WorkspaceInputFingerprint.tscomputedenvironmentHashfrom every defined entry of the environment.WorkspaceRequestLifecycle.#classifymaps anyenvironmentHashdifference from the startup fingerprint (the daemon'sprocess.env) toRestart. Only_RUSH_LIB_PATHwas normalized.ProductionDaemonRequestResolverran a second, independent whole-environment comparison (environmentIdentity) against the daemon's startup environment.{...process.env}verbatim.Fix
@alphaAPIsworkspaceFingerprintIgnoredEnvironmentVariablesandgetWorkspaceFingerprintEnvironmentEntries().captureWorkspaceInputFingerprintAsyncnow hashes only the normalized entries. The daemon startup fingerprint and every request fingerprint go through this function, so both sides get the same normalization.ProductionDaemonRequestResolver's environment check uses the same normalization, so the two checks can't disagree._ PWD OLDPWD SHLVL PS1 HISTFILE HISTSIZETERM TERM_PROGRAM TERM_PROGRAM_VERSION TERM_SESSION_ID COLORTERM COLUMNS LINES LS_COLORS WINDOWIDWSL_INTEROP WSLENV SSH_CLIENT SSH_CONNECTION SSH_TTY SSH_AUTH_SOCK TMUX TMUX_PANE STY XDG_SESSION_ID XDG_SESSION_TYPE DBUS_SESSION_BUS_ADDRESSINIT_CWD: Rush strips it from every lifecycle env and sets it explicitly where neededRUSH_DAEMONandRUSH_DAEMON_AUTO_STARTonly select or start a daemon;RUSH_DAEMON_EXPERIMENTALis read per request from the envelopeRUSH_*variables, e.g.RUSH_BUILD_CACHE_*,RUSH_PARALLELISM, and daemon resource settings such asRUSH_DAEMON_WATCHandRUSH_DAEMON_WARM_*NODE_OPTIONSand otherNODE_*variablesPATH(exact, since order matters),HOMEWhich environment do operations receive?
This part is unchanged; I'm documenting it here and in the rush-daemon README:
OperationGraphbuilds the operation env from{ ...process.env }, anddependsOnEnvVarshashing uses the same environment, so cache keys stay consistent with what operations see.PWD/SHLVL/_, a child shell recomputes them anyway.INIT_CWDis stripped by Rush. The rest are presentation or session handles.WSL_INTEROPandSSH_AUTH_SOCKcan go stale if the daemon's original shell exits. That was already true for any long-lived daemon.Tests
WorkspaceInputFingerprint.test.ts:PWD/OLDPWD/SHLVL/_/TERM/COLUMNS/WSL_INTEROP/SSH_*/INIT_CWD/RUSH_DAEMON*routing vars give the same hashFOO,RUSH_BUILD_CACHE_ENABLED,RUSH_BUILD_CACHE_WRITE_ALLOWED,RUSH_DAEMON_WATCH,NODE_OPTIONS,NPM_CONFIG_REGISTRY, a reorderedPATHandHOMEeach give a different hashWorkspaceReloadTierStatus.test.ts: on a warm daemon with a successor launcher configured, requests that differ only in volatile variables stay at tier 0 with the same generation, graph and pid, and no operation re-runs. The existing test for a real environment change still covers the restart path.Linux validation
Lab: WSL Ubuntu 24.04, Node 22. The patch was applied to a Linux-native clone of
main.rush build --to @rushstack/rush-cli-client --to @rushstack/rush-sdk: SUCCESS (lint clean)rush test --only @rushstack/rush-daemon --only @rushstack/rush-sdk: SUCCESSheft test --test-path-pattern WorkspaceInputFingerprint(rush-lib): 4/4 passedRepro (
$LAB/fixes/env-fingerprint-repro.sh): 12-projectmkws-synthworkspaces, all steps in one bash process with_pinned andRUSH_DAEMON=1. BEFORE = the unfixedrush-clientfrommain; AFTER =node <fix>/apps/rush-cli-client/bin/rush-client. "restored" = operations restored from the build cache; "0 ops" = warm skip with no output.OLDPWD=/xSHLVL=7TERM=dumb COLUMNS=91cd packages/p03(PWD/OLDPWD)WSL_INTEROP=/run/WSL/999_interopFOO=1(unknown var, restarts by design)RUSH_BUILD_CACHE_ENABLED=0(engine setting)After the fix, volatile-only differences keep the same daemon pid and re-run no operations (a warm skip at ~1s instead of a ~4s restart). Real engine inputs still restart the daemon.
Test note: the lifecycle test sends the same argv as the warm-up build. A different argv changes the command-parameter identity and is a tier-1 reload by design. A
samecontrol row in the test guards against confusing the two. PWD does not feed the command identity.Follow-ups (out of scope)
lastRestartReasonin daemon status (e.g. "environment: FOO added"). This needs a protocol/status field in the strict-lint protocol packages, so I left it for a separate PR.TERM/WSL_INTEROP/SSH_AUTH_SOCK.dependsOnEnvVars.Fixes #6047
This came out of the automated rushd Linux performance/behavior analysis (Rushd Hive, board BUG #20).