From 006563288823f22382e6e072999c0a696f8ea193 Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 17:35:24 +0000 Subject: [PATCH 01/84] rush-lib: allow daemon builds with plugins scoped to other commands; add daemon dogfooding workflow Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- README.md | 2 + apps/rush-cli-client/README.md | 9 +- .../rush/deploy-rush-daemon-dogfood.json | 15 ++ docs/rush/dogfooding-rush-daemon.md | 230 ++++++++++++++++++ docs/rush/environment-variables.md | 5 + libraries/rush-daemon/README.md | 9 +- .../src/ProductionDaemonRequestResolver.ts | 5 +- .../rush-lib/src/api/PhasedCommandEngine.ts | 21 +- .../src/api/WorkspaceInputFingerprint.ts | 10 + .../src/api/test/PhasedCommandEngine.test.ts | 205 ++++++++++++++++ .../test/WorkspaceInputFingerprint.test.ts | 46 ++++ .../cli/scriptActions/PhasedScriptAction.ts | 11 + .../PluginLoader/AutoinstallerPluginLoader.ts | 52 +++- .../src/pluginFramework/PluginManager.ts | 64 +++++ 14 files changed, 666 insertions(+), 18 deletions(-) create mode 100644 common/config/rush/deploy-rush-daemon-dogfood.json create mode 100644 docs/rush/dogfooding-rush-daemon.md create mode 100644 libraries/rush-lib/src/api/test/PhasedCommandEngine.test.ts diff --git a/README.md b/README.md index 1f46bad3013..981421ea761 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ for large scale TypeScript monorepos. - [API Documenter](https://api-extractor.com/pages/setup/generating_docs/) - use TSDoc comments to publish an API documentation website - [Lockfile Explorer](https://lfx.rushstack.io/) - investigate and solve version conflicts for PNPM lockfiles - [TSDoc](https://tsdoc.org/) - the standard for doc comments in TypeScript code +- [Dogfooding the Rush daemon](./docs/rush/dogfooding-rush-daemon.md) - contributor guide for building this repo + with the opt-in `rush-client` daemon, built from source ## Related Repos diff --git a/apps/rush-cli-client/README.md b/apps/rush-cli-client/README.md index 1d4cf98eb57..190d565ca47 100644 --- a/apps/rush-cli-client/README.md +++ b/apps/rush-cli-client/README.md @@ -3,6 +3,9 @@ Separate `rush-client` and `rushx-client` binaries, opt-in until cutover. Existing `rush`, `rushx`, and their reporter entrypoints are unchanged. +To try the daemon on the rushstack repository itself before a release contains it, follow the +[contributor dogfooding guide](../../docs/rush/dogfooding-rush-daemon.md). + ## Native frontend dependency The dependency on `@microsoft/rush` is intentional: it is the version-selecting @@ -116,9 +119,11 @@ changed configuration or command shape replaces the session and graph in the sam process. Environment, installed dependencies, implementation content, or selected Rush version changes require a process restart rather than patching the existing engine. Direct, inherited, and rig-based project configuration uses private native -loaders and is rechecked before execution. External plugins, `.env`, phased +loaders and is rechecked before execution. External plugins that participate in the +requested command (unassociated plugins, plugins associated with it, or plugin command-line +files that define it, its phases or parameters for either), `.env`, phased watch/install options, and unsupported event-hook scripts still use typed -pre-execution fallback; this does not exclude the built-in `install` and `update` +pre-execution fallback; plugins scoped only to other commands are permitted. This does not exclude the built-in `install` and `update` commands described above. The native Rush lock is held for preparation and each coalesced iteration, not while idle; native commands and `--no-daemon` can run after a completed request without stopping the daemon. diff --git a/common/config/rush/deploy-rush-daemon-dogfood.json b/common/config/rush/deploy-rush-daemon-dogfood.json new file mode 100644 index 00000000000..c83062bf534 --- /dev/null +++ b/common/config/rush/deploy-rush-daemon-dogfood.json @@ -0,0 +1,15 @@ +/** + * This configuration file defines a deployment scenario for use with the "rush deploy" command. + * For full documentation, please see https://rushjs.io + * + * The "rush-daemon-dogfood" scenario extracts a stable, self-contained copy of the source-built + * "rush-client" and "rushx-client" binaries, including the daemon and the Rush engine they bundle. + * Contributors use it to try the opt-in Rush daemon on this repository before a release contains it: + * a daemon started from the copy keeps working while the workspace rebuilds its own Rush projects. + * See docs/rush/dogfooding-rush-daemon.md. + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/deploy-scenario.schema.json", + + "deploymentProjectNames": ["@rushstack/rush-cli-client"] +} diff --git a/docs/rush/dogfooding-rush-daemon.md b/docs/rush/dogfooding-rush-daemon.md new file mode 100644 index 00000000000..0dce3be30fb --- /dev/null +++ b/docs/rush/dogfooding-rush-daemon.md @@ -0,0 +1,230 @@ +# Dogfooding the Rush daemon in rushstack + +This guide is for contributors who want to build the rushstack repository with the opt-in Rush daemon +(`rush-client`, from [`apps/rush-cli-client`](../../apps/rush-cli-client/README.md)) **before** a published +Rush release contains it. Ordinary `rush`, `rushx`, and CI are unaffected: they keep running in-process +unless you opt in explicitly. + +Nothing described here is published yet, so the client is built from source. The daemon must not run +directly from the workspace's build outputs: rebuilding `@microsoft/rush-lib`, `@rushstack/rush-daemon`, or +`@rushstack/rush-cli-client` would replace modules that the long-lived daemon is executing. Instead, +`rush deploy` extracts a stable, self-contained snapshot of the built client closure into the gitignored +`common/temp/rush-daemon-dogfood` folder, and the daemon runs from that snapshot. + +## Prerequisites + +- A supported Node.js version (see `nodeSupportedVersionRange` in `rush.json`) and Git. +- A completed install: `node common/scripts/install-run-rush.js install`. +- No `.env` file in the repository root or in `~/.rush-user/`. The daemon does not support `.env` + initialization and would fall back to in-process Rush. +- The source-built Rush version must equal the repository's `rushVersion`. Compare the output of + `node -p "require('./apps/rush/package.json').version"` with the `rushVersion` field in `rush.json`. + If they differ, see [Version skew](#version-skew-and-rush_preview_version). + +> **Do not export `RUSH_DAEMON` in a shell that also runs ordinary `rush`.** The published Rush release that +> `rush.json` currently selects rejects unrecognized `RUSH_` environment variables, so +> `node common/scripts/install-run-rush.js ...` fails with +> `The following environment variables were found with the "RUSH_" prefix, but they are not recognized`. +> The shell functions below set `RUSH_DAEMON=1` only for `rush-client` invocations. +> +> **Do not add a `daemon` block to `rush.json` yet.** The published schema rejects it, which would break +> ordinary `rush` and CI. Opting in with the environment variable is sufficient. + +## 1. Build the client closure + +```bash +node common/scripts/install-run-rush.js build --to @rushstack/rush-cli-client +``` + +## 2. Create the snapshot + +If a daemon from an earlier snapshot is running, stop it first (see [Refresh](#refresh-the-snapshot)). +Then extract the snapshot with the `rush-daemon-dogfood` deployment scenario +([`common/config/rush/deploy-rush-daemon-dogfood.json`](../../common/config/rush/deploy-rush-daemon-dogfood.json)): + +```bash +node common/scripts/install-run-rush.js deploy --scenario rush-daemon-dogfood --target-folder common/temp/rush-daemon-dogfood --overwrite +``` + +The snapshot contains copies of the built Rush projects and their npm dependencies, with links that stay +inside the snapshot. To confirm that the client resolves its engine from the snapshot rather than from the +workspace, run the following from the repository root. It must print a path under +`common/temp/rush-daemon-dogfood`: + +```bash +node -p "require('fs').realpathSync(require.resolve('@microsoft/rush-lib', { paths: [require('path').resolve('common/temp/rush-daemon-dogfood/apps/rush-cli-client')] }))" +``` + +The built-in cloud build-cache plugins are not part of the snapshot. That is fine for this repository, whose +build cache is `local-only`. + +## 3. Opt in and build + +Define a `rush-client` function in the terminal you will use, from the repository root. + +Bash: + +```bash +DOGFOOD_SNAPSHOT="$PWD/common/temp/rush-daemon-dogfood" +rush-client() { RUSH_DAEMON=1 node "$DOGFOOD_SNAPSHOT/apps/rush-cli-client/bin/rush-client" "$@"; } +``` + +PowerShell: + +```powershell +$DogfoodSnapshot = "$PWD\common\temp\rush-daemon-dogfood" +function rush-client { + $env:RUSH_DAEMON = '1' + try { node "$DogfoodSnapshot\apps\rush-cli-client\bin\rush-client" @args } finally { Remove-Item Env:RUSH_DAEMON } +} +``` + +Then build as usual. The first request starts the daemon and constructs the all-project graph; later +compatible requests from the same terminal reuse it: + +```bash +rush-client build --to @rushstack/tree-pattern +rush-client build --to @rushstack/tree-pattern +``` + +Any project selection that `rush build` accepts works, including projects in the `build-tests-subspace` +subspace. `rush-client rebuild` is also supported. + +## 4. Confirm that the daemon served the build + +Enabling the daemon is not proof that a build used it: unsupported requests silently or explicitly use +in-process Rush. Check all of the following: + +1. **No fallback.** When the client does not use the daemon, it runs native Rush, which prints the + `Rush Multi-Project Build Tool` banner. If the daemon was selected but could not serve the request, stderr + also contains a line starting with `rush-client:` and ending with `using in-process Rush.` A + daemon-served build prints neither. +2. **A warm graph.** `rush-client daemon status` prints one JSON object. `workspace.graphInitialized` must be + `true`. +3. **The same process.** `pid` and `workspace.generationToken` stay the same across requests from the same + terminal. `workspace.lastReloadTier` is `0` when the request reused the existing graph, and `1` after an + in-process reload (for example, the first request, or a configuration change). +4. **Parity.** For the same selection, ordinary Rush produces the same outputs. For example, after a + daemon-served build, `node common/scripts/install-run-rush.js rebuild --only @rushstack/tree-pattern` + leaves the files in `lib-commonjs`, `lib-dts`, `lib-esm`, and `dist` unchanged. + +A warm request whose selected operations are all up to date can finish without printing anything, and exits +with code `0`. + +For example, in one Bash terminal after steps 1–3: + +```bash +rush-client build --to @rushstack/tree-pattern 2>&1 | tee /tmp/dogfood-build.log +grep -E 'in-process Rush|Rush Multi-Project Build Tool' /tmp/dogfood-build.log && echo 'NOT served by the daemon' +rush-client daemon status # graphInitialized: true; note pid and generationToken +echo "// throwaway" >> libraries/tree-pattern/src/index.ts +rush-client build --to @rushstack/tree-pattern # rebuilds tree-pattern in the same daemon +rush-client daemon status # same pid and generationToken +git checkout -- libraries/tree-pattern/src/index.ts +``` + +In PowerShell, use `rush-client build --to @rushstack/tree-pattern *>&1 | Tee-Object dogfood-build.log` and +`Select-String -Path dogfood-build.log -Pattern 'in-process Rush', 'Rush Multi-Project Build Tool'`. + +## Use native Rush for one command + +Pass `--no-daemon` to run a single `rush-client` command in-process: + +```bash +rush-client build --no-daemon --to @rushstack/tree-pattern +``` + +Ordinary `node common/scripts/install-run-rush.js ...` commands also keep working while the daemon is +running. The daemon holds the Rush lock only while it prepares or executes a request, not while idle. + +## Refresh the snapshot + +The snapshot does not pick up later source changes to Rush itself; after changing or pulling changes to +`libraries/rush-lib`, `libraries/rush-daemon`, `libraries/rush-client-core`, or `apps/rush-cli-client`, refresh it. +Stop the daemon first, because `--overwrite` deletes the files that a running daemon executes: + +```bash +rush-client daemon stop +node common/scripts/install-run-rush.js build --to @rushstack/rush-cli-client +node common/scripts/install-run-rush.js deploy --scenario rush-daemon-dogfood --target-folder common/temp/rush-daemon-dogfood --overwrite +``` + +The daemon's identity is the canonical repository root plus the selected Rush version, so each checkout or +worktree has its own daemon, and `rush-client daemon ...` commands address the daemon for the checkout that +contains the current directory. + +## Stop and clean up + +```bash +rush-client daemon stop # prints state "shutdownAccepted" +rush-client daemon status # exits with code 1: no daemon is listening +rush-client daemon logs # launcher log, available even after the daemon stopped +``` + +Remove the snapshot with `rm -rf common/temp/rush-daemon-dogfood` (or `rush purge`, which clears all of +`common/temp`). Stop the daemon before purging or reinstalling. + +## Known limits + +- **Environment identity.** The complete request environment is a daemon input. A request whose + environment differs from the daemon's, such as one from another terminal (with a different `WT_SESSION`, + `WSL_INTEROP`, `TERM_SESSION_ID`, or `VSCODE_*` value) or after changing `PATH`, restarts the daemon before + anything runs and is then served by the new process. This is not a fallback, but it costs a cold start and + changes `pid`. Run related requests from the same terminal. +- **Only phased `build` and `rebuild` use the warm engine.** `rush start` (which always watches), + `--watch`, `--install`, `--variant`, and `--node-diagnostic-dir` stay native, as do build event-hook + scripts and reporter controls such as `--output`. Keep using ordinary Rush for `install`, `update`, and + other commands. `rushx-client` keeps scripts attached to a TTY in-process. +- **No persistent Heft or TypeScript workers.** Each operation still starts its Heft process; the daemon + saves Rush startup and graph construction, not compilation. The `usePersistentIpcRunners`/`daemonIpc` + mode requires a bundled, self-contained worker entry point, and Heft is not packaged that way. +- **Plugins.** This repository's only configured plugin, `@rushstack/rush-published-versions-json-plugin`, + is associated only with `record-published-versions` and is inert for builds. A plugin without + `associatedCommands`, a plugin associated with `build` or `rebuild`, or a plugin command-line that defines + the command, one of its phases, or a parameter for either would make those builds fall back to native Rush. +- **Windows.** Native Windows validation of the daemon code saw unresolved, intermittent failures in which + Git `hash-object --stdin-paths` exited with `0xC0000142` (DLL initialization failed) while the daemon + captured workspace snapshots under Jest. Direct fixtures did not reproduce it. If a daemon-served build + fails this way, retry it with `--no-daemon` and report the failure. +- **CI** stays in-process unless `RUSH_DAEMON=1` is set; do not set it in CI workflows. + +## Version skew and `RUSH_PREVIEW_VERSION` + +`rush-client` runs the daemon with its bundled engine only if that engine's version equals the selected Rush +version (`RUSH_PREVIEW_VERSION`, otherwise `rushVersion` in `rush.json`). Today both are the same, so the +snapshot's engine is used. If a release bumps `apps/rush` and `libraries/rush-lib` without updating +`rushVersion`, the client instead looks for a daemon-capable published release of the selected version. +None exists, so every build falls back to native Rush, with a `rush-client: ... Using in-process Rush.` +message (see [Confirm](#4-confirm-that-the-daemon-served-the-build)). + +To keep dogfooding in that state, select the snapshot's exact version for `rush-client` invocations only: + +```bash +DOGFOOD_VERSION=$(node -p "require('./common/temp/rush-daemon-dogfood/libraries/rush-lib/package.json').version") +rush-client() { RUSH_DAEMON=1 RUSH_PREVIEW_VERSION="$DOGFOOD_VERSION" node "$DOGFOOD_SNAPSHOT/apps/rush-cli-client/bin/rush-client" "$@"; } +``` + +Side effects: + +- The build runs with the snapshot's engine rather than the repository's selected release. +- `RUSH_PREVIEW_VERSION` is part of the daemon's identity and environment, so `rush-client daemon status` + and `rush-client daemon stop` address the matching daemon only when the same value is set. +- A native fallback, including `--no-daemon`, also selects that version. Because it equals the snapshot's + own frontend version, the snapshot's source-built Rush runs in-process, printing the + `RUSH_PREVIEW_VERSION` warning banner, rather than the repository's selected release. Use ordinary + `node common/scripts/install-run-rush.js ...` for builds with the selected release. +- Never export `RUSH_PREVIEW_VERSION` to ordinary `rush`; it would try to install that version from npm. + +## After a release contains the daemon + +Once a published Rush release includes the daemon, the standalone client, and the plugin narrowing that lets +this repository's command-scoped plugin coexist with daemon builds: + +1. Update `rushVersion` in `rush.json` to that release (or a later one). +2. Optionally add `"daemon": { "enabled": true }` to `rush.json`, so that `rush-client` uses the daemon + without `RUSH_DAEMON=1`. CI still stays in-process unless it sets `RUSH_DAEMON=1`, and `RUSH_DAEMON=0` + opts a shell out. +3. Install the published client whose engine matches `rushVersion`, for example + `npm install --global @rushstack/rush-cli-client`, and run `rush-client` directly instead of the snapshot. +4. Stop any snapshot daemon and delete `common/temp/rush-daemon-dogfood`. The `rush-daemon-dogfood` + deployment scenario remains useful for trying unreleased daemon changes. diff --git a/docs/rush/environment-variables.md b/docs/rush/environment-variables.md index b1664e9c731..35f266c2f24 100644 --- a/docs/rush/environment-variables.md +++ b/docs/rush/environment-variables.md @@ -50,6 +50,11 @@ Environment-only opt-in avoids adding a `daemon` block that an older Rush schema `RUSH_PREVIEW_VERSION` continues to select the requested Rush engine; a matching daemon must actually be available. A client never relabels its bundled engine to satisfy a different version. +Releases that predate these variables reject unknown `RUSH_` names. While a repository's `rushVersion` +selects such a release, set `RUSH_DAEMON` only for `rush-client` invocations rather than exporting it +to a shell that also runs ordinary `rush`. The [contributor dogfooding guide](./dogfooding-rush-daemon.md) +shows this for the rushstack repository. + See the [client configuration and management reference](../../apps/rush-cli-client/README.md) for launcher selection, lifecycle commands, persistent-worker restrictions and status fields. diff --git a/libraries/rush-daemon/README.md b/libraries/rush-daemon/README.md index 713b69bec1f..fde6efde309 100644 --- a/libraries/rush-daemon/README.md +++ b/libraries/rush-daemon/README.md @@ -108,9 +108,14 @@ the graph/cache settings with the construction snapshot. Changed inherited or ri generation reload before execution, even outside watcher roots or in ignored `node_modules` files. The retained graph and its cache policy are never patched in place. -External Rush plugins, `.env` initialization, watch/install/variant +External Rush plugins that Rush would initialize for the requested command (plugins without `associatedCommands`, +or associated with that command) or whose cached command-line.json defines that command, one of its phases, or a +parameter associated with either are rejected, as are `.env` initialization, watch/install/variant and diagnostic-directory options, build event-hook scripts (unless explicitly ignored), and arbitrary global -commands are rejected by the phased path, not silently bypassed. Native Rushx is handled separately below. +commands; they are rejected by the phased path, not silently bypassed. Configured plugins that are scoped only to +other commands are inert for the build and are permitted; their autoinstaller `package.json` and cached manifest and +command-line files are workspace definitions, so changing them reloads the generation. An unreadable manifest or +command-line file is rejected. Native Rushx is handled separately below. For phased commands, a changed request environment requires a new process, including Rush/cache policy variables. These restrictions remain until the corresponding initialization, environment, and resource-lifetime contracts are request-scoped. diff --git a/libraries/rush-daemon/src/ProductionDaemonRequestResolver.ts b/libraries/rush-daemon/src/ProductionDaemonRequestResolver.ts index 7a1793510a4..9f81bba821a 100644 --- a/libraries/rush-daemon/src/ProductionDaemonRequestResolver.ts +++ b/libraries/rush-daemon/src/ProductionDaemonRequestResolver.ts @@ -39,8 +39,9 @@ import type { IWorkspaceResolverLifecycle } from './WorkspaceResolverLifecycle'; * @remarks * A host is pinned to its first command and non-selection parameters. Incompatible parameters, * environments, or graph inputs are rejected before scheduling; no request is retried automatically. - * The initial supported surface excludes external plugins, .env initialization, install/watch, - * event-hook scripts, and rushx/global commands. Use the unchanged native CLI for those surfaces. + * The initial supported surface excludes external plugins that participate in the requested command, + * .env initialization, install/watch, event-hook scripts, and rushx/global commands. Use the unchanged + * native CLI for those surfaces. * @beta */ export class ProductionDaemonRequestResolver implements IDaemonRequestResolver { diff --git a/libraries/rush-lib/src/api/PhasedCommandEngine.ts b/libraries/rush-lib/src/api/PhasedCommandEngine.ts index 0fefc3fddf4..98b48a9b4c6 100644 --- a/libraries/rush-lib/src/api/PhasedCommandEngine.ts +++ b/libraries/rush-lib/src/api/PhasedCommandEngine.ts @@ -49,8 +49,10 @@ export interface IParsePhasedCommandOptions { * * @remarks * The initial engine surface deliberately rejects watch/install, event-hook scripts, .env files, and - * externally supplied plugins. Those require request-scoped initialization and asynchronous disposal - * contracts before they can safely run in a shared process. Native graph/cache plugins are not replaced. + * external plugins that Rush would initialize for the command or whose command-line.json shapes it. + * Those require request-scoped initialization and asynchronous disposal contracts before they can + * safely run in a shared process. Plugins associated only with other commands are inert and permitted. + * Native graph/cache plugins are not replaced. * @alpha */ export class PhasedCommandEngine { @@ -74,9 +76,6 @@ export class PhasedCommandEngine { if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) { throw new Error('Command help must be handled by the native CLI, not by an engine request.'); } - if (rushConfiguration._rushPluginsConfiguration.configuration.plugins.length > 0) { - throw new Error('Daemon engine execution does not yet support external Rush plugins. Use --no-daemon.'); - } for (const folder of [rushConfiguration.rushJsonFolder, RushUserConfiguration.getRushUserFolderPath()]) { if (FileSystem.exists(path.join(folder, '.env'))) { throw new Error('Daemon engine execution does not yet support .env initialization. Use --no-daemon.'); @@ -91,6 +90,18 @@ export class PhasedCommandEngine { if (!(action instanceof PhasedScriptAction) || !['build', 'rebuild'].includes(action.actionName)) { throw new Error('The production daemon engine currently supports native build and rebuild only.'); } + // Plugins which the command would never initialize, and whose command-line.json does not shape + // this command, cannot affect a shared engine. Every other external plugin still requires native Rush. + const participatingPlugins: ReadonlyArray = parser.pluginManager.getPluginsParticipatingInCommand( + action.actionName, + action.schedulablePhaseNames + ); + if (participatingPlugins.length > 0) { + throw new Error( + `Daemon engine execution does not yet support Rush plugins that participate in "${action.actionName}": ` + + `${participatingPlugins.join('; ')}. Use --no-daemon.` + ); + } action.validateEngineCommand(); return new PhasedCommandEngine(parser, action); } diff --git a/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts b/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts index 3a6271a3bfe..dbfa93eb929 100644 --- a/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts +++ b/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts @@ -13,6 +13,7 @@ import type { RushConfiguration } from './RushConfiguration'; import type { RushConfigurationProject } from './RushConfigurationProject'; import { RushProjectConfiguration } from './RushProjectConfiguration'; import { getDaemonIpcImplementationIdentityAsync } from '../logic/operations/DaemonIpcConfiguration'; +import { AutoinstallerPluginLoader } from '../pluginFramework/PluginLoader/AutoinstallerPluginLoader'; /** Stable inputs which distinguish reusable, reloadable, and process-bound workspace state. @alpha */ export interface IWorkspaceInputFingerprint { @@ -152,6 +153,15 @@ export async function captureWorkspaceInputFingerprintAsync( for (const filename of configurationFiles) { (isProcessBoundConfiguration(filename) ? installation : definitions).add(filename); } + // Configured plugins shape the command-line parser even when they are never loaded for a command. + for (const pluginConfiguration of rushConfiguration._rushPluginsConfiguration.configuration.plugins) { + for (const filename of AutoinstallerPluginLoader.getPluginShapeFilePaths( + rushConfiguration, + pluginConfiguration + )) { + definitions.add(filename); + } + } for (const project of rushJson.projects) { const projectFolder: string = path.resolve(root, project.projectFolder); if (!Path.isUnderOrEqual(projectFolder, root)) { diff --git a/libraries/rush-lib/src/api/test/PhasedCommandEngine.test.ts b/libraries/rush-lib/src/api/test/PhasedCommandEngine.test.ts new file mode 100644 index 00000000000..1faae1ece93 --- /dev/null +++ b/libraries/rush-lib/src/api/test/PhasedCommandEngine.test.ts @@ -0,0 +1,205 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; + +import { NoOpTerminalProvider } from '@rushstack/terminal'; + +import { PhasedCommandEngine } from '../PhasedCommandEngine'; +import { RushConfiguration } from '../RushConfiguration'; + +const PACKAGE_NAME: string = '@example/rush-example-plugin'; +const PLUGIN_NAME: string = 'rush-example-plugin'; +const PLUGIN_COMMAND: string = 'record-example'; + +interface IPluginFixture { + readonly associatedCommands?: string[]; + readonly commandLineJson?: object; + readonly writeManifest?: boolean; +} + +// Mirrors the command-scoped plugin that rushstack configures in common/config/rush/rush-plugins.json. +const COMMAND_SCOPED_COMMAND_LINE_JSON: object = { + commands: [{ commandKind: 'globalPlugin', name: PLUGIN_COMMAND, summary: 'Records an example.' }], + parameters: [ + { + parameterKind: 'string', + longName: '--output-path', + argumentName: 'FILE_PATH', + description: 'The output path.', + associatedCommands: [PLUGIN_COMMAND] + } + ] +}; + +const REPO_COMMAND_LINE_JSON: object = { + commands: [ + { + commandKind: 'phased', + name: 'build', + summary: 'Build', + phases: ['_phase:build'], + enableParallelism: true, + incremental: true + } + ], + phases: [{ name: '_phase:build', dependencies: { upstream: ['_phase:build'] } }] +}; + +function createRepo(plugin: IPluginFixture): string { + const folder: string = fs.mkdtempSync(path.join(os.tmpdir(), 'rush-engine-plugins-')); + const write = (relativePath: string, json: object): void => { + const filename: string = path.join(folder, relativePath); + fs.mkdirSync(path.dirname(filename), { recursive: true }); + fs.writeFileSync(filename, JSON.stringify(json)); + }; + write('rush.json', { rushVersion: '5.179.0', pnpmVersion: '10.27.0', projects: [] }); + write('common/config/rush/command-line.json', REPO_COMMAND_LINE_JSON); + write('common/config/rush/rush-plugins.json', { + plugins: [{ packageName: PACKAGE_NAME, pluginName: PLUGIN_NAME, autoinstallerName: 'plugins' }] + }); + write('common/autoinstallers/plugins/package.json', { + name: 'plugins', + version: '1.0.0', + private: true, + dependencies: { [PACKAGE_NAME]: '1.0.0' } + }); + const storeFolder: string = `common/autoinstallers/plugins/rush-plugins/${PACKAGE_NAME}`; + if (plugin.writeManifest !== false) { + write(`${storeFolder}/rush-plugin-manifest.json`, { + plugins: [ + { + pluginName: PLUGIN_NAME, + description: 'An example plugin.', + entryPoint: './lib/index.js', + associatedCommands: plugin.associatedCommands, + commandLineJsonFilePath: './command-line.json' + } + ] + }); + } + if (plugin.commandLineJson) { + write(`${storeFolder}/${PLUGIN_NAME}/command-line.json`, plugin.commandLineJson); + } + return folder; +} + +async function parseBuildAsync(folder: string, argv: string[] = ['build']): Promise { + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + path.join(folder, 'rush.json') + ); + return await PhasedCommandEngine.parseAsync({ + argv, + cwd: folder, + rushConfiguration, + terminalProvider: new NoOpTerminalProvider() + }); +} + +describe(PhasedCommandEngine.name, () => { + const folders: string[] = []; + function createTestRepo(plugin: IPluginFixture): string { + const folder: string = createRepo(plugin); + folders.push(folder); + return folder; + } + + afterEach(() => { + for (const folder of folders.splice(0)) { + fs.rmSync(folder, { recursive: true, force: true }); + } + }); + + it('accepts a plugin that is associated with, and only shapes, a different command', async () => { + const folder: string = createTestRepo({ + associatedCommands: [PLUGIN_COMMAND], + commandLineJson: COMMAND_SCOPED_COMMAND_LINE_JSON + }); + for (const commandName of ['build', 'rebuild']) { + const command: PhasedCommandEngine = await parseBuildAsync(folder, [commandName]); + expect(command.commandName).toBe(commandName); + } + }); + + it('accepts a plugin associated with no commands and without a command-line.json', async () => { + const folder: string = createTestRepo({ associatedCommands: [] }); + const command: PhasedCommandEngine = await parseBuildAsync(folder); + expect(command.commandName).toBe('build'); + }); + + it('rejects an unassociated plugin, which Rush initializes for every command', async () => { + const folder: string = createTestRepo({ commandLineJson: COMMAND_SCOPED_COMMAND_LINE_JSON }); + await expect(parseBuildAsync(folder)).rejects.toThrow( + `Daemon engine execution does not yet support Rush plugins that participate in "build": ` + + `"${PLUGIN_NAME}" (${PACKAGE_NAME}) is initialized for every command. Use --no-daemon.` + ); + }); + + it('rejects a plugin associated with the requested command', async () => { + const folder: string = createTestRepo({ + associatedCommands: [PLUGIN_COMMAND, 'build'], + commandLineJson: COMMAND_SCOPED_COMMAND_LINE_JSON + }); + await expect(parseBuildAsync(folder)).rejects.toThrow( + `"${PLUGIN_NAME}" (${PACKAGE_NAME}) is associated with "build"` + ); + // The association is specific to the requested command. + await expect(parseBuildAsync(folder, ['rebuild'])).resolves.toBeInstanceOf(PhasedCommandEngine); + }); + + it('rejects a plugin parameter associated with the requested command', async () => { + const folder: string = createTestRepo({ + associatedCommands: [PLUGIN_COMMAND], + commandLineJson: { + commands: [ + { commandKind: 'globalPlugin', name: PLUGIN_COMMAND, summary: 'Records an example.' }, + { commandKind: 'bulk', name: 'build', summary: 'Build', enableParallelism: true } + ], + parameters: [ + { + parameterKind: 'flag', + longName: '--example-flag', + description: 'An example flag.', + associatedCommands: [PLUGIN_COMMAND, 'build'] + } + ] + } + }); + // A plugin-defined build replaces the repository build, so the repository must not also define it. + fs.rmSync(path.join(folder, 'common/config/rush/command-line.json')); + await expect(parseBuildAsync(folder)).rejects.toThrow( + `"${PLUGIN_NAME}" (${PACKAGE_NAME}) associates "--example-flag" with "build"` + ); + }); + + it('rejects a plugin parameter associated with a phase of the requested command', async () => { + const folder: string = createTestRepo({ + associatedCommands: [PLUGIN_COMMAND], + commandLineJson: { + ...COMMAND_SCOPED_COMMAND_LINE_JSON, + phases: [{ name: '_phase:build' }], + parameters: [ + { + parameterKind: 'flag', + longName: '--example-flag', + description: 'An example flag.', + associatedCommands: [PLUGIN_COMMAND], + associatedPhases: ['_phase:build'] + } + ] + } + }); + await expect(parseBuildAsync(folder)).rejects.toThrow( + `"${PLUGIN_NAME}" (${PACKAGE_NAME}) associates "--example-flag" with the "_phase:build" phase` + ); + }); + + it('fails closed when a configured plugin manifest cannot be read', async () => { + const folder: string = createTestRepo({ writeManifest: false }); + await expect(parseBuildAsync(folder)).rejects.toThrow( + `"${PLUGIN_NAME}" (${PACKAGE_NAME}): its manifest could not be read` + ); + }); +}); diff --git a/libraries/rush-lib/src/api/test/WorkspaceInputFingerprint.test.ts b/libraries/rush-lib/src/api/test/WorkspaceInputFingerprint.test.ts index 8a19aba368c..3d6e97dd0da 100644 --- a/libraries/rush-lib/src/api/test/WorkspaceInputFingerprint.test.ts +++ b/libraries/rush-lib/src/api/test/WorkspaceInputFingerprint.test.ts @@ -41,6 +41,52 @@ describe('workspace input fingerprints', () => { } }); + it('reloads when a configured plugin shape outside common/config changes', async () => { + const folder: string = fs.mkdtempSync(path.join(os.tmpdir(), 'rush-fingerprint-')); + try { + const write = (relativePath: string, content: string): void => { + const filename: string = path.join(folder, relativePath); + fs.mkdirSync(path.dirname(filename), { recursive: true }); + fs.writeFileSync(filename, content); + }; + write('rush.json', JSON.stringify({ rushVersion: '5.179.0', pnpmVersion: '10.27.0', projects: [] })); + write( + 'common/config/rush/rush-plugins.json', + JSON.stringify({ + plugins: [{ packageName: '@example/plugin', pluginName: 'example', autoinstallerName: 'plugins' }] + }) + ); + const store: string = 'common/autoinstallers/plugins/rush-plugins/@example/plugin'; + write('common/autoinstallers/plugins/package.json', '{"name":"plugins","version":"1.0.0"}'); + write(`${store}/rush-plugin-manifest.json`, '{"plugins":[]}'); + write(`${store}/example/command-line.json`, '{"commands":[]}'); + const rushConfiguration: RushConfiguration = RushConfiguration.loadFromConfigurationFile( + path.join(folder, 'rush.json') + ); + const runtimeCache: WorkspaceRuntimeFingerprintCache = new WorkspaceRuntimeFingerprintCache(); + const captureAsync = (): Promise => + captureWorkspaceInputFingerprintAsync({ rushConfiguration, runtimeCache, environment: {} }); + + let previous: IWorkspaceInputFingerprint = await captureAsync(); + for (const [relativePath, content] of [ + [`${store}/example/command-line.json`, '{"commands":[],"parameters":[]}'], + [`${store}/rush-plugin-manifest.json`, '{"plugins":[{}]}'], + ['common/autoinstallers/plugins/package.json', '{"name":"plugins","version":"1.0.1"}'] + ]) { + write(relativePath, content); + const next: IWorkspaceInputFingerprint = await captureAsync(); + expect(classifyWorkspaceInputChange(previous, next)).toBe(WorkspaceInputChangeTier.Reload); + previous = next; + } + fs.rmSync(path.join(folder, `${store}/example/command-line.json`)); + expect(classifyWorkspaceInputChange(previous, await captureAsync())).toBe( + WorkspaceInputChangeTier.Reload + ); + } finally { + fs.rmSync(folder, { recursive: true, force: true }); + } + }); + it('classifies content, configuration and process-bound identities', () => { const current: IWorkspaceInputFingerprint = { configurationHash: 'configuration', diff --git a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts index 2818bca1f7a..2f1eb707b9e 100644 --- a/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts +++ b/libraries/rush-lib/src/cli/scriptActions/PhasedScriptAction.ts @@ -345,6 +345,17 @@ export class PhasedScriptAction extends BaseScriptAction i await this.#runAsync(); } + /** The names of every phase this command can schedule, including dependency and watch phases. */ + public get schedulablePhaseNames(): ReadonlySet { + const phaseNames: Set = new Set(); + for (const phases of [this.#originalPhases, this.#initialPhases, this.#watchPhases]) { + for (const phase of phases) { + phaseNames.add(phase.name); + } + } + return phaseNames; + } + public validateEngineCommand(): void { if ( this.#alwaysWatch || diff --git a/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts b/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts index 7528fe72ea7..8c464bcc6de 100644 --- a/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts +++ b/libraries/rush-lib/src/pluginFramework/PluginLoader/AutoinstallerPluginLoader.ts @@ -4,6 +4,7 @@ import * as path from 'node:path'; import { + FileConstants, FileSystem, JsonFile, NewlineKind, @@ -13,6 +14,7 @@ import { } from '@rushstack/node-core-library'; import type { IRushPluginConfiguration } from '../../api/RushPluginsConfiguration'; +import type { RushConfiguration } from '../../api/RushConfiguration'; import { Autoinstaller } from '../../logic/Autoinstaller'; import { RushConstants } from '../../logic/RushConstants'; import { @@ -53,7 +55,29 @@ export class AutoinstallerPluginLoader extends PluginLoaderBase\rush-plugins` */ public static getPluginAutoinstallerStorePath(autoinstaller: Autoinstaller): string { - return path.join(autoinstaller.folderFullPath, 'rush-plugins'); + return getStorePath(autoinstaller.folderFullPath); + } + + /** + * Files outside `common/config` from which Rush reads a configured plugin's manifest and command-line + * shape without loading the plugin: the autoinstaller package.json, and the cached manifest and + * command-line.json that `rush update` copies into the autoinstaller's store. + */ + public static getPluginShapeFilePaths( + rushConfiguration: RushConfiguration, + pluginConfiguration: IRushPluginConfiguration + ): string[] { + const { autoinstallerName, packageName, pluginName } = pluginConfiguration; + const autoinstallerFolder: string = path.join( + rushConfiguration.commonAutoinstallersFolder, + autoinstallerName + ); + const storePath: string = getStorePath(autoinstallerFolder); + return [ + path.join(autoinstallerFolder, FileConstants.PackageJson), + getCachedManifestPath(storePath, packageName), + getCachedCommandLineJsonFilePath(storePath, packageName, pluginName) + ]; } public update(): void { @@ -155,19 +179,33 @@ export class AutoinstallerPluginLoader extends PluginLoaderBase + ): ReadonlyArray { + const reasons: string[] = []; + for (const pluginLoader of this.#autoinstallerPluginLoaders) { + const pluginLabel: string = `"${pluginLoader.pluginName}" (${pluginLoader.packageName})`; + let associatedCommands: ReadonlyArray | undefined; + try { + associatedCommands = pluginLoader.pluginManifest.associatedCommands; + } catch (error) { + reasons.push(`${pluginLabel}: its manifest could not be read: ${(error as Error).message}`); + continue; + } + if (!associatedCommands) { + reasons.push(`${pluginLabel} is initialized for every command`); + } else if (associatedCommands.includes(commandName)) { + reasons.push(`${pluginLabel} is associated with "${commandName}"`); + } + + let commandLineConfiguration: CommandLineConfiguration | undefined; + try { + commandLineConfiguration = pluginLoader.getCommandLineConfiguration(); + } catch (error) { + reasons.push(`${pluginLabel}: its command-line.json could not be read: ${(error as Error).message}`); + continue; + } + if (!commandLineConfiguration) { + continue; + } + if (commandLineConfiguration.commands.has(commandName)) { + reasons.push(`${pluginLabel} defines the "${commandName}" command`); + } + for (const phaseName of commandLineConfiguration.phases.keys()) { + if (phaseNames.has(phaseName)) { + reasons.push(`${pluginLabel} defines the "${phaseName}" phase`); + } + } + for (const parameter of commandLineConfiguration.parameters) { + const { longName, associatedPhases } = parameter; + if (parameter.associatedCommands?.includes(commandName)) { + reasons.push(`${pluginLabel} associates "${longName}" with "${commandName}"`); + } + for (const phaseName of associatedPhases ?? []) { + if (phaseNames.has(phaseName)) { + reasons.push(`${pluginLabel} associates "${longName}" with the "${phaseName}" phase`); + } + } + } + } + return reasons; + } + #initializePlugins(pluginLoaders: PluginLoaderBase[]): void { for (const pluginLoader of pluginLoaders) { const pluginName: string = pluginLoader.pluginName; From eafab7e6f500ab4a8b7315d7c6f7bd4b0684ca72 Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 17:36:32 +0000 Subject: [PATCH 02/84] Add change files Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...arkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json | 10 ++++++++++ ...arkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json | 10 ++++++++++ ...arkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 common/changes/@microsoft/rush/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json create mode 100644 common/changes/@rushstack/rush-daemon/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json diff --git a/common/changes/@microsoft/rush/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json b/common/changes/@microsoft/rush/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json new file mode 100644 index 00000000000..2cc94db24b4 --- /dev/null +++ b/common/changes/@microsoft/rush/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Allow daemon build and rebuild requests in repositories whose external Rush plugins are associated only with other commands, and reload the daemon generation when a configured plugin's autoinstaller package.json, cached manifest, or command-line.json changes.", + "type": "patch" + } + ], + "packageName": "@microsoft/rush" +} diff --git a/common/changes/@rushstack/rush-cli-client/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json b/common/changes/@rushstack/rush-cli-client/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json new file mode 100644 index 00000000000..452883b2c56 --- /dev/null +++ b/common/changes/@rushstack/rush-cli-client/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-cli-client", + "comment": "Document the plugin narrowing and link the rushstack contributor guide for dogfooding the daemon from source.", + "type": "none" + } + ], + "packageName": "@rushstack/rush-cli-client" +} diff --git a/common/changes/@rushstack/rush-daemon/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json b/common/changes/@rushstack/rush-daemon/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json new file mode 100644 index 00000000000..d8691f7d626 --- /dev/null +++ b/common/changes/@rushstack/rush-daemon/thelarkinn-dogfood-rush-daemon_2026-09-23-17-36-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-daemon", + "comment": "Document that external Rush plugins scoped only to other commands no longer prevent daemon builds.", + "type": "none" + } + ], + "packageName": "@rushstack/rush-daemon" +} From 0da26eae013b8fdd5e3ee46ffcf4c529fe8bcf9f Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 18:02:16 +0000 Subject: [PATCH 03/84] docs: record Windows validation and daemon process check in dogfooding guide Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/rush/dogfooding-rush-daemon.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/rush/dogfooding-rush-daemon.md b/docs/rush/dogfooding-rush-daemon.md index 0dce3be30fb..ea347993885 100644 --- a/docs/rush/dogfooding-rush-daemon.md +++ b/docs/rush/dogfooding-rush-daemon.md @@ -103,7 +103,8 @@ in-process Rush. Check all of the following: `true`. 3. **The same process.** `pid` and `workspace.generationToken` stay the same across requests from the same terminal. `workspace.lastReloadTier` is `0` when the request reused the existing graph, and `1` after an - in-process reload (for example, the first request, or a configuration change). + in-process reload (for example, the first request, or a configuration change). The command line of that + `pid` runs `SelectedDaemonBootstrap.js` from `common/temp/rush-daemon-dogfood`, not from the workspace. 4. **Parity.** For the same selection, ordinary Rush produces the same outputs. For example, after a daemon-served build, `node common/scripts/install-run-rush.js rebuild --only @rushstack/tree-pattern` leaves the files in `lib-commonjs`, `lib-dts`, `lib-esm`, and `dist` unchanged. @@ -184,8 +185,9 @@ Remove the snapshot with `rm -rf common/temp/rush-daemon-dogfood` (or `rush purg the command, one of its phases, or a parameter for either would make those builds fall back to native Rush. - **Windows.** Native Windows validation of the daemon code saw unresolved, intermittent failures in which Git `hash-object --stdin-paths` exited with `0xC0000142` (DLL initialization failed) while the daemon - captured workspace snapshots under Jest. Direct fixtures did not reproduce it. If a daemon-served build - fails this way, retry it with `--no-daemon` and report the failure. + captured workspace snapshots under Jest. Direct fixtures did not reproduce it, and it did not occur while + this workflow was validated on native Windows. The cause is unresolved, so treat it as an open risk: if a + daemon-served build fails this way, retry it with `--no-daemon` and report the failure. - **CI** stays in-process unless `RUSH_DAEMON=1` is set; do not set it in CI workflows. ## Version skew and `RUSH_PREVIEW_VERSION` From ef9a3fcd48c1ec0ffd4cb9caf058404aced41013 Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 21:21:07 +0000 Subject: [PATCH 04/84] rush-daemon: hide console windows of tools the detached Windows daemon starts Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...bprocess-consoles_2026-09-23-21-30-00.json | 10 ++ .../src/SelectedDaemonBootstrap.ts | 5 + .../src/WindowsSubprocessConsoles.ts | 89 ++++++++++++++ .../test/WindowsSubprocessConsoles.test.ts | 112 ++++++++++++++++++ 4 files changed, 216 insertions(+) create mode 100644 common/changes/@rushstack/rush-daemon/thelarkinn-windows-hide-subprocess-consoles_2026-09-23-21-30-00.json create mode 100644 libraries/rush-daemon/src/WindowsSubprocessConsoles.ts create mode 100644 libraries/rush-daemon/src/test/WindowsSubprocessConsoles.test.ts diff --git a/common/changes/@rushstack/rush-daemon/thelarkinn-windows-hide-subprocess-consoles_2026-09-23-21-30-00.json b/common/changes/@rushstack/rush-daemon/thelarkinn-windows-hide-subprocess-consoles_2026-09-23-21-30-00.json new file mode 100644 index 00000000000..1ab4d195b38 --- /dev/null +++ b/common/changes/@rushstack/rush-daemon/thelarkinn-windows-hide-subprocess-consoles_2026-09-23-21-30-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-daemon", + "comment": "On Windows, keep tools started by the detached daemon (Git, tar and operation shells) from opening visible console windows.", + "type": "patch" + } + ], + "packageName": "@rushstack/rush-daemon" +} \ No newline at end of file diff --git a/libraries/rush-daemon/src/SelectedDaemonBootstrap.ts b/libraries/rush-daemon/src/SelectedDaemonBootstrap.ts index 7b8a6b1d2cf..ee8d7b1e977 100644 --- a/libraries/rush-daemon/src/SelectedDaemonBootstrap.ts +++ b/libraries/rush-daemon/src/SelectedDaemonBootstrap.ts @@ -13,6 +13,7 @@ import { type IDaemonInstallationMetadata, type IInstalledDaemonLauncher } from './DaemonInstallation'; +import { installWindowsHideDefault } from './WindowsSubprocessConsoles'; async function mainAsync(): Promise { const [mode, packageJsonPath, expectedVersion, repoRoot] = process.argv.slice(2); @@ -95,6 +96,10 @@ async function mainAsync(): Promise { configured.daemon, process.env ); + if (process.platform === 'win32') { + // This process was started detached, without a console; keep its tools from opening console windows. + installWindowsHideDefault(); + } await serveRushDaemonAsync({ repoRoot, rushVersion: installation.rushVersion, diff --git a/libraries/rush-daemon/src/WindowsSubprocessConsoles.ts b/libraries/rush-daemon/src/WindowsSubprocessConsoles.ts new file mode 100644 index 00000000000..9551a0f04b0 --- /dev/null +++ b/libraries/rush-daemon/src/WindowsSubprocessConsoles.ts @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import childProcessModule from 'node:child_process'; +import { syncBuiltinESMExports } from 'node:module'; + +type ChildProcessFunction = (...args: unknown[]) => unknown; + +/** + * The index of the options argument of a child_process function, given its arguments. + */ +type OptionsIndexSelector = (args: ReadonlyArray) => number; + +const installedTargets: WeakSet = new WeakSet(); + +/** `(command, args?, options?, callback?)`: spawn, spawnSync, execFile, execFileSync and fork. */ +function afterOptionalArgs(args: ReadonlyArray): number { + return Array.isArray(args[1]) || (args[1] === undefined && args.length > 2) || args[1] === null ? 2 : 1; +} + +/** `(command, options?, callback?)`: exec and execSync. */ +function afterCommand(): number { + return 1; +} + +const OPTIONS_INDEX_BY_FUNCTION: ReadonlyMap = new Map([ + ['spawn', afterOptionalArgs], + ['spawnSync', afterOptionalArgs], + ['execFile', afterOptionalArgs], + ['execFileSync', afterOptionalArgs], + ['fork', afterOptionalArgs], + ['exec', afterCommand], + ['execSync', afterCommand] +]); + +/** + * Returns the arguments with `windowsHide: true` added to the options, unless the caller specified it. + */ +export function withWindowsHideDefault(args: ReadonlyArray, optionsIndex: number): unknown[] { + const result: unknown[] = [...args]; + const options: unknown = result[optionsIndex]; + if (options === undefined || options === null) { + result[optionsIndex] = { windowsHide: true }; + } else if (typeof options === 'function') { + // A callback occupies the options position, as in `execFile(file, callback)`. + result.splice(optionsIndex, 0, { windowsHide: true }); + } else if ( + typeof options === 'object' && + (options as { windowsHide?: unknown }).windowsHide === undefined + ) { + result[optionsIndex] = { ...options, windowsHide: true }; + } + return result; +} + +/** + * Makes every child process started by this process default to `windowsHide: true`. + * + * @remarks + * The standalone daemon is started as a detached Windows process, which has no console. Windows then + * gives each console program it starts (Git, tar, the shell running an operation, and so on) a new, + * visible console window, unless that program is started with `windowsHide`. A hidden child instead + * owns a windowless console, which its own descendants inherit, so defaulting this process's direct + * children is sufficient. Library and plugin code starts those children, so the default is applied + * where they all resolve it: the `node:child_process` module. Explicit `windowsHide` values are kept. + * + * Only the dedicated daemon process calls this, and only on Windows. It is idempotent. + */ +export function installWindowsHideDefault( + target: Record = childProcessModule as unknown as Record +): void { + if (installedTargets.has(target)) { + return; + } + installedTargets.add(target); + for (const [name, getOptionsIndex] of OPTIONS_INDEX_BY_FUNCTION) { + const original: ChildProcessFunction = target[name] as ChildProcessFunction; + if (typeof original !== 'function') { + continue; + } + target[name] = function (this: unknown, ...args: unknown[]): unknown { + return original.apply(this, withWindowsHideDefault(args, getOptionsIndex(args))); + }; + } + if (target === (childProcessModule as unknown as Record)) { + // Also update the named exports seen by ECMAScript modules. + syncBuiltinESMExports(); + } +} diff --git a/libraries/rush-daemon/src/test/WindowsSubprocessConsoles.test.ts b/libraries/rush-daemon/src/test/WindowsSubprocessConsoles.test.ts new file mode 100644 index 00000000000..3241c18ca5e --- /dev/null +++ b/libraries/rush-daemon/src/test/WindowsSubprocessConsoles.test.ts @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as childProcess from 'node:child_process'; + +import { installWindowsHideDefault, withWindowsHideDefault } from '../WindowsSubprocessConsoles'; + +describe('Windows subprocess console defaults', () => { + const callback = (): void => {}; + + it('adds windowsHide to every child_process call shape unless the caller chose a value', () => { + expect(withWindowsHideDefault(['git'], 1)).toEqual(['git', { windowsHide: true }]); + expect(withWindowsHideDefault(['git', ['status']], 2)).toEqual([ + 'git', + ['status'], + { windowsHide: true } + ]); + expect(withWindowsHideDefault(['git', ['status'], { cwd: '/repo' }], 2)).toEqual([ + 'git', + ['status'], + { cwd: '/repo', windowsHide: true } + ]); + expect(withWindowsHideDefault(['git', { stdio: 'pipe' }], 1)).toEqual([ + 'git', + { stdio: 'pipe', windowsHide: true } + ]); + expect(withWindowsHideDefault(['git', callback], 1)).toEqual(['git', { windowsHide: true }, callback]); + expect(withWindowsHideDefault(['git', ['status'], callback], 2)).toEqual([ + 'git', + ['status'], + { windowsHide: true }, + callback + ]); + expect(withWindowsHideDefault(['git', ['status'], { windowsHide: false }], 2)).toEqual([ + 'git', + ['status'], + { windowsHide: false } + ]); + expect(withWindowsHideDefault(['git', ['status'], { windowsHide: undefined }], 2)).toEqual([ + 'git', + ['status'], + { windowsHide: true } + ]); + }); + + it('does not modify the caller options object', () => { + const options: childProcess.SpawnOptions = { cwd: '/repo' }; + withWindowsHideDefault(['git', [], options], 2); + expect(options).toEqual({ cwd: '/repo' }); + }); + + it('wraps each child_process function once, preserving results and argument positions', () => { + const calls: Array<[string, unknown[]]> = []; + const record = + (name: string): ((...args: unknown[]) => string) => + (...args: unknown[]): string => { + calls.push([name, args]); + return name; + }; + const target: Record = { + spawn: record('spawn'), + spawnSync: record('spawnSync'), + execFile: record('execFile'), + execFileSync: record('execFileSync'), + fork: record('fork'), + exec: record('exec'), + execSync: record('execSync'), + unrelated: record('unrelated') + }; + const unrelated: unknown = target.unrelated; + installWindowsHideDefault(target); + const spawn: unknown = target.spawn; + installWindowsHideDefault(target); + expect(target.spawn).toBe(spawn); + expect(target.unrelated).toBe(unrelated); + + const call = (name: string, ...args: unknown[]): unknown => + (target[name] as (...callArgs: unknown[]) => unknown)(...args); + expect(call('spawn', 'git', ['status'])).toBe('spawn'); + call('spawnSync', 'git', { encoding: 'utf8' }); + call('execFile', 'git', ['status'], callback); + call('execFileSync', 'git', ['status'], { windowsHide: false }); + call('fork', './worker.js', ['--flag']); + call('exec', 'git status', callback); + call('execSync', 'git status'); + call('unrelated', 'git'); + expect(calls).toEqual([ + ['spawn', ['git', ['status'], { windowsHide: true }]], + ['spawnSync', ['git', { encoding: 'utf8', windowsHide: true }]], + ['execFile', ['git', ['status'], { windowsHide: true }, callback]], + ['execFileSync', ['git', ['status'], { windowsHide: false }]], + ['fork', ['./worker.js', ['--flag'], { windowsHide: true }]], + ['exec', ['git status', { windowsHide: true }, callback]], + ['execSync', ['git status', { windowsHide: true }]], + ['unrelated', ['git']] + ]); + }); + + it('still runs real child processes through the wrapped functions', () => { + // A copy, so that this test does not change the child_process module shared with other tests. + const target: Record = { ...childProcess }; + installWindowsHideDefault(target); + const spawnSync: typeof childProcess.spawnSync = target.spawnSync as typeof childProcess.spawnSync; + const result: childProcess.SpawnSyncReturns = spawnSync( + process.execPath, + ['-e', 'process.stdout.write("ok")'], + { encoding: 'utf8' } + ); + expect(result.status).toBe(0); + expect(result.stdout).toBe('ok'); + }); +}); From 278a3d276f56ed67e0420abf8fa94d5598c7ea5a Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 21:26:25 +0000 Subject: [PATCH 05/84] docs: describe hidden Windows tool consoles and silent up-to-date builds Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/rush/dogfooding-rush-daemon.md | 8 ++++++-- libraries/rush-daemon/README.md | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/rush/dogfooding-rush-daemon.md b/docs/rush/dogfooding-rush-daemon.md index ea347993885..58a549ee197 100644 --- a/docs/rush/dogfooding-rush-daemon.md +++ b/docs/rush/dogfooding-rush-daemon.md @@ -109,8 +109,9 @@ in-process Rush. Check all of the following: daemon-served build, `node common/scripts/install-run-rush.js rebuild --only @rushstack/tree-pattern` leaves the files in `lib-commonjs`, `lib-dts`, `lib-esm`, and `dist` unchanged. -A warm request whose selected operations are all up to date can finish without printing anything, and exits -with code `0`. +A warm request whose selected operations are all up to date finishes **without printing anything**, and exits +with code `0`. Unlike ordinary Rush, the daemon client does not yet print a summary, so "no output" after a +build that already ran means everything was up to date; `rush-client daemon status` confirms that it was served. For example, in one Bash terminal after steps 1–3: @@ -188,6 +189,9 @@ Remove the snapshot with `rm -rf common/temp/rush-daemon-dogfood` (or `rush purg captured workspace snapshots under Jest. Direct fixtures did not reproduce it, and it did not occur while this workflow was validated on native Windows. The cause is unresolved, so treat it as an open risk: if a daemon-served build fails this way, retry it with `--no-daemon` and report the failure. + The daemon runs without a console, so it starts its tools (Git, tar and operation shells) with hidden + windows. Daemons started from a snapshot older than that change open a visible terminal window for each + tool; if you see that, [refresh the snapshot](#refresh-the-snapshot). - **CI** stays in-process unless `RUSH_DAEMON=1` is set; do not set it in CI workflows. ## Version skew and `RUSH_PREVIEW_VERSION` diff --git a/libraries/rush-daemon/README.md b/libraries/rush-daemon/README.md index fde6efde309..9c50aa7bb36 100644 --- a/libraries/rush-daemon/README.md +++ b/libraries/rush-daemon/README.md @@ -142,6 +142,11 @@ an installation using native Rush package-install APIs; the host does not instal packages during restart. Selection probes foreign runtimes in isolation, and launch rechecks the actual engine version and protocol before binding. An unavailable or incompatible installation is never impersonated by the bundled engine. + +On Windows the standalone launcher starts the daemon detached, without a console. Before serving, the launched +daemon therefore makes `windowsHide: true` the default for every `node:child_process` call that does not choose a +value, so Git, tar, operation shells and plugin tools do not each open a visible console window. Their descendants +inherit the resulting windowless console. Embedded hosts, which own their process, are not changed. Embedded `RushDaemonHost` users can provide `getSuccessorLaunchAsync`, returning the existing core `IDaemonStartCommand` plus the expected daemon implementation version. Selection is validated before shutdown; an unavailable selected Rush version fails explicitly and is never run by the current engine under a false version. From 8122fae49f321209cce44d95f0d530abb8a58550 Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 21:43:33 +0000 Subject: [PATCH 06/84] rush-cli-client: start without loading the Rush engine; cheaper runtime fingerprint The client only needs to locate rush.json, read daemon settings, parse rushx arguments and build its daemon start command. Move those into small rush-lib and rush-daemon modules so the client no longer loads the whole engine for every invocation, and load version selection and rushx discovery only when needed. The runtime fingerprint now also covers the bundle chunks under dist, which contain the implementation, and recomputes a resolved path only when a file stat changes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../rush-cli-client/src/bundledRushVersion.ts | 11 ++ .../src/daemonConnectionOptions.ts | 18 +-- apps/rush-cli-client/src/launchClient.ts | 22 ++-- apps/rush-cli-client/src/routing.ts | 7 +- .../rush-daemon/src/DaemonLaunchCommand.ts | 49 ++++++++ .../src/VersionSelectedDaemonLauncher.ts | 51 ++------- .../rush-lib/src/api/RushConfiguration.ts | 33 +----- .../rush-lib/src/api/RushJsonLocation.ts | 44 +++++++ .../src/api/WorkspaceInputFingerprint.ts | 9 +- .../rush-lib/src/cli/RushXCommandLine.ts | 101 +---------------- .../src/cli/RushXCommandLineArguments.ts | 107 ++++++++++++++++++ 11 files changed, 266 insertions(+), 186 deletions(-) create mode 100644 apps/rush-cli-client/src/bundledRushVersion.ts create mode 100644 libraries/rush-daemon/src/DaemonLaunchCommand.ts create mode 100644 libraries/rush-lib/src/api/RushJsonLocation.ts create mode 100644 libraries/rush-lib/src/cli/RushXCommandLineArguments.ts diff --git a/apps/rush-cli-client/src/bundledRushVersion.ts b/apps/rush-cli-client/src/bundledRushVersion.ts new file mode 100644 index 00000000000..67a69f30bf9 --- /dev/null +++ b/apps/rush-cli-client/src/bundledRushVersion.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { JsonFile } from '@rushstack/node-core-library'; + +/** + * The version of the bundled Rush engine, which is `Rush.version`, read without loading the engine. + */ +export const BUNDLED_RUSH_VERSION: string = JsonFile.load( + require.resolve('@microsoft/rush-lib/package.json') +).version; diff --git a/apps/rush-cli-client/src/daemonConnectionOptions.ts b/apps/rush-cli-client/src/daemonConnectionOptions.ts index ab4b5297f85..61de58bd3c0 100644 --- a/apps/rush-cli-client/src/daemonConnectionOptions.ts +++ b/apps/rush-cli-client/src/daemonConnectionOptions.ts @@ -3,17 +3,17 @@ import * as fs from 'node:fs'; -import { Rush } from '@microsoft/rush-lib'; import { JsonFile } from '@rushstack/node-core-library'; import type { IConnectOrStartDaemonOptions } from '@rushstack/rush-client-core'; import { computeDaemonWorkspaceKey, resolveDaemonPathsFromProcess } from '@rushstack/rush-daemon-transport'; import { readDaemonInstallationMetadata } from '@rushstack/rush-daemon/lib/DaemonInstallation'; import { DaemonLauncherUnavailableError, - getSelectedDaemonStartCommand, - selectDaemonLauncherAsync, - type IVersionSelectedDaemonLaunch -} from '@rushstack/rush-daemon/lib/VersionSelectedDaemonLauncher'; + getSelectedDaemonStartCommand +} from '@rushstack/rush-daemon/lib/DaemonLaunchCommand'; +import type { IVersionSelectedDaemonLaunch } from '@rushstack/rush-daemon/lib/VersionSelectedDaemonLauncher'; + +import { BUNDLED_RUSH_VERSION } from './bundledRushVersion'; export function getDaemonConnectionOptions( repoRoot: string, @@ -57,9 +57,13 @@ export async function getDaemonConnectionOptionsAsync( false ); if (!autoStart) return { paths: options.paths }; - // The bundled runtime is already loaded here; its bootstrap re-attests before binding. - if (rushVersion === Rush.version) + // The bundled runtime needs no selection; its bootstrap re-attests before binding. + if (rushVersion === BUNDLED_RUSH_VERSION) return getDaemonConnectionOptions(repoRoot, rushVersion, environment, true); + // Selecting another installation uses the engine's installer APIs, so it is loaded only when needed. + const { selectDaemonLauncherAsync } = await import( + '@rushstack/rush-daemon/lib/VersionSelectedDaemonLauncher' + ); const launch: IVersionSelectedDaemonLaunch = await selectDaemonLauncherAsync({ repoRoot: fs.realpathSync.native(repoRoot), rushVersion, diff --git a/apps/rush-cli-client/src/launchClient.ts b/apps/rush-cli-client/src/launchClient.ts index b739b9fadf7..a8ef4a97dde 100644 --- a/apps/rush-cli-client/src/launchClient.ts +++ b/apps/rush-cli-client/src/launchClient.ts @@ -4,12 +4,11 @@ import * as path from 'node:path'; import { - Rush, - RushConfiguration, daemonEnvironmentVariables, resolveDaemonConfiguration, type IDaemonConfigurationJson -} from '@microsoft/rush-lib'; +} from '@microsoft/rush-lib/lib/api/DaemonConfiguration'; +import { tryFindRushJsonLocation } from '@microsoft/rush-lib/lib/api/RushJsonLocation'; import { JsonFile } from '@rushstack/node-core-library'; import { DaemonClientError, @@ -22,9 +21,9 @@ import { } from '@rushstack/rush-client-core'; import type { DaemonVerbosity, IDaemonRequestEnvelope } from '@rushstack/rush-daemon-protocol'; import { ConsoleTerminalProvider } from '@rushstack/terminal'; -import { MinimalRushConfiguration } from '@microsoft/rush/lib/MinimalRushConfiguration'; -import { DaemonLauncherUnavailableError } from '@rushstack/rush-daemon/lib/VersionSelectedDaemonLauncher'; +import { DaemonLauncherUnavailableError } from '@rushstack/rush-daemon/lib/DaemonLaunchCommand'; +import { BUNDLED_RUSH_VERSION } from './bundledRushVersion'; import { executeDaemonCommandAsync } from './daemonCommands'; import { ClientOperationRenderer } from './ClientOperationRenderer'; import { getDaemonConnectionOptionsAsync } from './daemonConnectionOptions'; @@ -39,7 +38,7 @@ interface IWorkspaceJson { export async function launchClientAsync(rushx: boolean): Promise { const cwd: string = process.cwd(); const environment: Readonly = Object.freeze({ ...process.env }); - const rushJsonPath: string | undefined = RushConfiguration.tryFindRushJsonLocation({ startingFolder: cwd }); + const rushJsonPath: string | undefined = tryFindRushJsonLocation({ startingFolder: cwd }); const workspace: IWorkspaceJson | undefined = rushJsonPath ? JsonFile.load(rushJsonPath) : undefined; const config: Readonly> = resolveDaemonConfiguration( workspace?.daemon, @@ -52,7 +51,8 @@ export async function launchClientAsync(rushx: boolean): Promise { rushx, hasTerminal: !!(process.stdin.isTTY || process.stdout.isTTY || process.stderr.isTTY) }); - const selectedVersion: string = environment.RUSH_PREVIEW_VERSION ?? workspace?.rushVersion ?? Rush.version; + const selectedVersion: string = + environment.RUSH_PREVIEW_VERSION ?? workspace?.rushVersion ?? BUNDLED_RUSH_VERSION; if (!rushx && route.commandName === 'daemon') { if ((route.argv[1] === 'start' || route.argv[1] === 'restart') && process.argv.includes('--no-daemon')) { throw new Error(`--no-daemon cannot be combined with daemon ${route.argv[1]}.`); @@ -145,7 +145,11 @@ export async function launchClientAsync(rushx: boolean): Promise { } }; try { - if (rushx) MinimalRushConfiguration.loadFromDefaultLocation((line) => discoveryLines.push(line)); + if (rushx) { + // Rushx discovery output needs the frontend's configuration loader, which loads the engine. + const { MinimalRushConfiguration } = await import('@microsoft/rush/lib/MinimalRushConfiguration'); + MinimalRushConfiguration.loadFromDefaultLocation((line) => discoveryLines.push(line)); + } await renderer.initializeAsync(); outcome = await executeWithDaemonRestartAsync(client, connection, { request, @@ -207,7 +211,7 @@ function launchInProcess(argv: ReadonlyArray, rushx: boolean, selectedVe const executable: string = rushx ? 'rushx' : 'rush'; const rushFolder: string = path.dirname(require.resolve('@microsoft/rush/package.json')); process.argv = [process.execPath, path.join(rushFolder, 'bin', executable), ...argv]; - if (selectedVersion !== Rush.version) { + if (selectedVersion !== BUNDLED_RUSH_VERSION) { // Old Rush releases reject new RUSH_* names. Only strip this launcher's own inputs; // the request snapshot was captured earlier and is never mutated. for (const name of [...Object.values(daemonEnvironmentVariables), 'RUSH_DAEMON_EXPERIMENTAL']) { diff --git a/apps/rush-cli-client/src/routing.ts b/apps/rush-cli-client/src/routing.ts index 310960249bf..d08f192d970 100644 --- a/apps/rush-cli-client/src/routing.ts +++ b/apps/rush-cli-client/src/routing.ts @@ -2,7 +2,10 @@ // See LICENSE in the project root for license information. import type { IDaemonRequestAdmissionOptions } from '@rushstack/rush-daemon-protocol'; -import { RushXCommand, type IRushXCommandLineArguments } from '@microsoft/rush-lib'; +import { + parseRushXCommandLineArguments, + type IRushXCommandLineArguments +} from '@microsoft/rush-lib/lib/cli/RushXCommandLineArguments'; import { parseClientAdmissionControls, type IClientAdmissionControls } from './ClientAdmissionControls'; @@ -53,7 +56,7 @@ export function selectClientRoute(options: IClientRouteOptions): IClientRoute { ...(separator < 0 ? [] : controls.argv.slice(separator)) ]; const rushxArguments: IRushXCommandLineArguments | undefined = options.rushx - ? RushXCommand.parseArguments(argv, options.environment) + ? parseRushXCommandLineArguments(argv, options.environment) : undefined; const commandName: string | undefined = rushxArguments ? rushxArguments.commandName || undefined : argv[0]; const reporterControls: boolean = diff --git a/libraries/rush-daemon/src/DaemonLaunchCommand.ts b/libraries/rush-daemon/src/DaemonLaunchCommand.ts new file mode 100644 index 00000000000..950a8d3c4ce --- /dev/null +++ b/libraries/rush-daemon/src/DaemonLaunchCommand.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import type { IDaemonStartCommand } from '@rushstack/rush-client-core'; + +import type { IInstalledDaemonLauncher } from './DaemonInstallation'; + +// These launch definitions do not load the Rush engine, so that a client can start or reach its +// bundled daemon cheaply. Version selection and installation remain in VersionSelectedDaemonLauncher. + +export interface IDaemonLauncherContext { + readonly repoRoot: string; + readonly rushVersion: string; + readonly environment: Readonly; +} + +export class DaemonLauncherUnavailableError extends Error { + public readonly installation: IInstalledDaemonLauncher | undefined; + + public constructor(rushVersion: string, reason: string, installation?: IInstalledDaemonLauncher) { + super(`Cannot launch selected Rush ${rushVersion}: ${reason} Use native Rush instead.`); + this.name = 'DaemonLauncherUnavailableError'; + this.installation = installation; + } +} + +export function getSelectedDaemonStartCommand( + daemonPackageJsonPath: string, + context: IDaemonLauncherContext +): IDaemonStartCommand { + return { + command: process.execPath, + args: [ + require.resolve('./SelectedDaemonBootstrap'), + '--launch', + daemonPackageJsonPath, + context.rushVersion, + context.repoRoot + ], + cwd: context.repoRoot, + environment: Object.freeze( + Object.fromEntries( + Object.entries(context.environment).filter( + (entry): entry is [string, string] => entry[1] !== undefined + ) + ) + ) + }; +} diff --git a/libraries/rush-daemon/src/VersionSelectedDaemonLauncher.ts b/libraries/rush-daemon/src/VersionSelectedDaemonLauncher.ts index f931b842775..4a4d8e0d5cb 100644 --- a/libraries/rush-daemon/src/VersionSelectedDaemonLauncher.ts +++ b/libraries/rush-daemon/src/VersionSelectedDaemonLauncher.ts @@ -22,29 +22,24 @@ import { type IDaemonInstallationMetadata, type IInstalledDaemonLauncher } from './DaemonInstallation'; +import { + DaemonLauncherUnavailableError, + getSelectedDaemonStartCommand, + type IDaemonLauncherContext +} from './DaemonLaunchCommand'; -const DAEMON_PACKAGE: string = '@rushstack/rush-daemon'; +export { + DaemonLauncherUnavailableError, + getSelectedDaemonStartCommand, + type IDaemonLauncherContext +} from './DaemonLaunchCommand'; -export interface IDaemonLauncherContext { - readonly repoRoot: string; - readonly rushVersion: string; - readonly environment: Readonly; -} +const DAEMON_PACKAGE: string = '@rushstack/rush-daemon'; export interface IVersionSelectedDaemonLaunch extends IInstalledDaemonLauncher { readonly startCommand: IDaemonStartCommand; } -export class DaemonLauncherUnavailableError extends Error { - public readonly installation: IInstalledDaemonLauncher | undefined; - - public constructor(rushVersion: string, reason: string, installation?: IInstalledDaemonLauncher) { - super(`Cannot launch selected Rush ${rushVersion}: ${reason} Use native Rush instead.`); - this.name = 'DaemonLauncherUnavailableError'; - this.installation = installation; - } -} - /** Native-style, node-specific cache, resolved against the captured environment without mutating process.env. */ export function getDaemonVersionCacheFolder(environment: Readonly): string { const globalFolder: string = @@ -63,30 +58,6 @@ export function assertExactDaemonVersion(version: string): void { } } -export function getSelectedDaemonStartCommand( - daemonPackageJsonPath: string, - context: IDaemonLauncherContext -): IDaemonStartCommand { - return { - command: process.execPath, - args: [ - require.resolve('./SelectedDaemonBootstrap'), - '--launch', - daemonPackageJsonPath, - context.rushVersion, - context.repoRoot - ], - cwd: context.repoRoot, - environment: Object.freeze( - Object.fromEntries( - Object.entries(context.environment).filter( - (entry): entry is [string, string] => entry[1] !== undefined - ) - ) - ) - }; -} - /** Probes in a fresh process so loading a foreign Rush engine cannot alter the caller's SDK/global state. */ export async function inspectDaemonLauncherAsync( daemonPackageJsonPath: string, diff --git a/libraries/rush-lib/src/api/RushConfiguration.ts b/libraries/rush-lib/src/api/RushConfiguration.ts index 2d2408a9eb5..e1c562677ab 100644 --- a/libraries/rush-lib/src/api/RushConfiguration.ts +++ b/libraries/rush-lib/src/api/RushConfiguration.ts @@ -47,6 +47,7 @@ import { CustomTipsConfiguration } from './CustomTipsConfiguration'; import { SubspacesConfiguration } from './SubspacesConfiguration'; import { Subspace } from './Subspace'; import { resolveDaemonConfiguration, type IDaemonConfigurationJson } from './DaemonConfiguration'; +import { tryFindRushJsonLocation } from './RushJsonLocation'; const MINIMUM_SUPPORTED_RUSH_JSON_VERSION: string = '0.0.0'; const DEFAULT_BRANCH: string = 'main'; @@ -1100,37 +1101,11 @@ export class RushConfiguration { * Find the rush.json location and return the path, or undefined if a rush.json can't be found. * * @privateRemarks - * Keep this in sync with `findRushJsonLocation` in `rush-sdk/src/index.ts`. + * Keep this in sync with `findRushJsonLocation` in `rush-sdk/src/index.ts`. The implementation lives in + * `RushJsonLocation.ts` so that callers can find rush.json without loading the rest of the engine. */ public static tryFindRushJsonLocation(options?: ITryFindRushJsonLocationOptions): string | undefined { - const optionsIn: ITryFindRushJsonLocationOptions = options || {}; - const verbose: boolean = optionsIn.showVerbose || false; - let currentFolder: string = optionsIn.startingFolder || process.cwd(); - let parentFolder: string = path.dirname(currentFolder); - - // look upwards at parent folders until we find a folder containing rush.json, - // or we reach the root directory without finding a rush.json file - while (parentFolder && parentFolder !== currentFolder) { - const rushJsonFilename: string = path.join(currentFolder, RushConstants.rushJsonFilename); - if (FileSystem.exists(rushJsonFilename)) { - if (currentFolder !== optionsIn.startingFolder && verbose) { - // eslint-disable-next-line no-console - console.log('Found configuration in ' + rushJsonFilename); - } - - if (verbose) { - // eslint-disable-next-line no-console - console.log(''); - } - - return rushJsonFilename; - } - currentFolder = parentFolder; - parentFolder = path.dirname(currentFolder); - } - - // no match - return undefined; + return tryFindRushJsonLocation(options); } /** diff --git a/libraries/rush-lib/src/api/RushJsonLocation.ts b/libraries/rush-lib/src/api/RushJsonLocation.ts new file mode 100644 index 00000000000..1c3758473ad --- /dev/null +++ b/libraries/rush-lib/src/api/RushJsonLocation.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { FileSystem } from '@rushstack/node-core-library'; + +import { RushConstants } from '../logic/RushConstants'; +import type { ITryFindRushJsonLocationOptions } from './RushConfiguration'; + +/** + * Implements `RushConfiguration.tryFindRushJsonLocation` without loading the rest of the Rush engine, + * for callers such as the standalone client that only need the location. + */ +export function tryFindRushJsonLocation(options?: ITryFindRushJsonLocationOptions): string | undefined { + const optionsIn: ITryFindRushJsonLocationOptions = options || {}; + const verbose: boolean = optionsIn.showVerbose || false; + let currentFolder: string = optionsIn.startingFolder || process.cwd(); + let parentFolder: string = path.dirname(currentFolder); + + // look upwards at parent folders until we find a folder containing rush.json, + // or we reach the root directory without finding a rush.json file + while (parentFolder && parentFolder !== currentFolder) { + const rushJsonFilename: string = path.join(currentFolder, RushConstants.rushJsonFilename); + if (FileSystem.exists(rushJsonFilename)) { + if (currentFolder !== optionsIn.startingFolder && verbose) { + // eslint-disable-next-line no-console + console.log('Found configuration in ' + rushJsonFilename); + } + + if (verbose) { + // eslint-disable-next-line no-console + console.log(''); + } + + return rushJsonFilename; + } + currentFolder = parentFolder; + parentFolder = path.dirname(currentFolder); + } + + // no match + return undefined; +} diff --git a/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts b/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts index dbfa93eb929..9852d782275 100644 --- a/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts +++ b/libraries/rush-lib/src/api/WorkspaceInputFingerprint.ts @@ -68,16 +68,17 @@ export class WorkspaceRuntimeFingerprintCache { const entries: ReadonlyArray[] = []; for (const filename of Array.from(filenames).sort()) { try { + // statSync follows links, so dev and ino identify the file that is loaded. Its resolved path is + // recomputed whenever that identity changes, which avoids a costly realpath for every unchanged file. const stat: fsSync.BigIntStats = fsSync.statSync(filename, { bigint: true }); - const realPath: string = fsSync.realpathSync(filename); - const stamp: string = `${realPath}:${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`; + const stamp: string = `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`; let cached: { stamp: string; entry: ReadonlyArray } | undefined = this._files.get(filename); if (cached?.stamp !== stamp) { cached = { stamp, entry: [ filename, - realPath, + fsSync.realpathSync(filename), createHash('sha256').update(fsSync.readFileSync(filename)).digest('hex') ] }; @@ -183,6 +184,8 @@ export async function captureWorkspaceInputFingerprintAsync( const runtimePaths: string[] = [ path.join(packageFolder, 'package.json'), path.join(packageFolder, 'lib-commonjs'), + // In a bundled Rush, lib-commonjs only forwards to the bundle chunks, which contain the implementation. + path.join(packageFolder, 'dist'), ...(options.runtimePaths ?? []) ]; const runtimeHash: string = (options.runtimeCache ?? new WorkspaceRuntimeFingerprintCache())._hashPaths( diff --git a/libraries/rush-lib/src/cli/RushXCommandLine.ts b/libraries/rush-lib/src/cli/RushXCommandLine.ts index 60342cbfac3..8dd0ca6a3b2 100644 --- a/libraries/rush-lib/src/cli/RushXCommandLine.ts +++ b/libraries/rush-lib/src/cli/RushXCommandLine.ts @@ -37,39 +37,12 @@ import { RushConstants } from '../logic/RushConstants'; import { PnpmSyncUtilities } from '../utilities/PnpmSyncUtilities'; import { initializeDotEnv, loadDotEnvForEnvironment } from '../logic/dotenv'; import { escapeArgumentIfNeeded } from '../utilities/executionUtilities'; +import { + parseRushXCommandLineArguments as _parseCommandLineArguments, + type IRushXCommandLineArguments +} from './RushXCommandLineArguments'; -/** Native Rushx arguments. Options after the command belong to the script. @beta */ -export interface IRushXCommandLineArguments { - /** - * Flag indicating whether to suppress any rushx startup information. - */ - quiet: boolean; - - /** - * Flag indicating whether the user has asked for help. - */ - help: boolean; - - /** - * Flag indicating whether the user has requested debug mode. - */ - isDebug: boolean; - - /** - * Flag indicating whether the user wants to not call hooks. - */ - ignoreHooks: boolean; - - /** - * The command to run (i.e., the target "script" in package.json.) - */ - commandName: string; - - /** - * Any additional arguments/parameters passed after the command name. - */ - commandArgs: string[]; -} +export type { IRushXCommandLineArguments } from './RushXCommandLineArguments'; /** Explicit process state and an optional owned asynchronous spawn seam for native Rushx. @beta */ export interface IRushXCommandOptions { @@ -406,70 +379,6 @@ function _getLifecycleConfiguration( }; } -function _parseCommandLineArguments( - args: ReadonlyArray, - environment: Readonly, - reportUnknownArguments?: (message: string) => void -): IRushXCommandLineArguments { - const unknownArgs: string[] = []; - - let help: boolean = false; - let quiet: boolean = false; - let commandName: string = ''; - let isDebug: boolean = false; - let ignoreHooks: boolean = false; - const commandArgs: string[] = []; - - for (let index: number = 0; index < args.length; index++) { - const argValue: string = args[index]; - - if (!commandName) { - if (argValue === '-q' || argValue === '--quiet') { - quiet = true; - } else if (argValue === '-h' || argValue === '--help') { - help = true; - } else if (argValue === '-d' || argValue === '--debug') { - isDebug = true; - } else if (argValue === '--ignore-hooks') { - ignoreHooks = true; - } else if (argValue.startsWith('-')) { - unknownArgs.push(args[index]); - } else { - commandName = args[index]; - } - } else { - commandArgs.push(args[index]); - } - } - - const quietModeValue: string | undefined = environment[EnvironmentVariableNames.RUSH_QUIET_MODE]; - if (quietModeValue === '1' || quietModeValue === 'true') { - quiet = true; - } - - if (!commandName) { - help = true; - } - - if (unknownArgs.length > 0) { - // Future TODO: Instead of just displaying usage info, we could display a - // specific error about the unknown flag the user tried to pass to rushx. - reportUnknownArguments?.( - Colorize.red(`Unknown arguments: ${unknownArgs.map((x) => JSON.stringify(x)).join(', ')}`) - ); - help = true; - } - - return { - help, - quiet, - isDebug, - ignoreHooks, - commandName, - commandArgs - }; -} - function _showUsage( packageJson: IPackageJson, projectCommandSet: ProjectCommandSet, diff --git a/libraries/rush-lib/src/cli/RushXCommandLineArguments.ts b/libraries/rush-lib/src/cli/RushXCommandLineArguments.ts new file mode 100644 index 00000000000..376e0423bfe --- /dev/null +++ b/libraries/rush-lib/src/cli/RushXCommandLineArguments.ts @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { Colorize } from '@rushstack/terminal'; + +import { EnvironmentVariableNames } from '../api/EnvironmentConfiguration'; + +/** Native Rushx arguments. Options after the command belong to the script. @beta */ +export interface IRushXCommandLineArguments { + /** + * Flag indicating whether to suppress any rushx startup information. + */ + quiet: boolean; + + /** + * Flag indicating whether the user has asked for help. + */ + help: boolean; + + /** + * Flag indicating whether the user has requested debug mode. + */ + isDebug: boolean; + + /** + * Flag indicating whether the user wants to not call hooks. + */ + ignoreHooks: boolean; + + /** + * The command to run (i.e., the target "script" in package.json.) + */ + commandName: string; + + /** + * Any additional arguments/parameters passed after the command name. + */ + commandArgs: string[]; +} + +/** + * Parses native Rushx arguments. This module does not load the rest of the Rush engine, so that callers + * such as the standalone client can route a Rushx invocation cheaply. + */ +export function parseRushXCommandLineArguments( + args: ReadonlyArray, + environment: Readonly, + reportUnknownArguments?: (message: string) => void +): IRushXCommandLineArguments { + const unknownArgs: string[] = []; + + let help: boolean = false; + let quiet: boolean = false; + let commandName: string = ''; + let isDebug: boolean = false; + let ignoreHooks: boolean = false; + const commandArgs: string[] = []; + + for (let index: number = 0; index < args.length; index++) { + const argValue: string = args[index]; + + if (!commandName) { + if (argValue === '-q' || argValue === '--quiet') { + quiet = true; + } else if (argValue === '-h' || argValue === '--help') { + help = true; + } else if (argValue === '-d' || argValue === '--debug') { + isDebug = true; + } else if (argValue === '--ignore-hooks') { + ignoreHooks = true; + } else if (argValue.startsWith('-')) { + unknownArgs.push(args[index]); + } else { + commandName = args[index]; + } + } else { + commandArgs.push(args[index]); + } + } + + const quietModeValue: string | undefined = environment[EnvironmentVariableNames.RUSH_QUIET_MODE]; + if (quietModeValue === '1' || quietModeValue === 'true') { + quiet = true; + } + + if (!commandName) { + help = true; + } + + if (unknownArgs.length > 0) { + // Future TODO: Instead of just displaying usage info, we could display a + // specific error about the unknown flag the user tried to pass to rushx. + reportUnknownArguments?.( + Colorize.red(`Unknown arguments: ${unknownArgs.map((x) => JSON.stringify(x)).join(', ')}`) + ); + help = true; + } + + return { + help, + quiet, + isDebug, + ignoreHooks, + commandName, + commandArgs + }; +} From 667af71a51ab17d37e463d3c0dcf70445a2c91cd Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 22:04:41 +0000 Subject: [PATCH 07/84] rush-client-core: let the startup helper finish a slow daemon start The detached helper waited for readiness only until the requesting client deadline (15 seconds). A slower first start, such as the first start from a freshly deployed snapshot on Windows, left its startup reservation behind. Every later client then refused to use the ready daemon and fell back to native Rush after 15 seconds. The helper now waits for a live launcher for at least 120 seconds; the reservation is still retained if the launcher exits or never becomes ready. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...ient-startup-perf_2026-09-23-22-20-00.json | 10 ++++++++++ ...ient-startup-perf_2026-09-23-22-20-00.json | 10 ++++++++++ ...ient-startup-perf_2026-09-23-22-20-00.json | 10 ++++++++++ ...ient-startup-perf_2026-09-23-22-20-00.json | 10 ++++++++++ libraries/rush-client-core/README.md | 5 ++++- .../src/connectOrStartDaemon.ts | 9 ++++++++- .../src/test/connectOrStartDaemon.test.ts | 19 +++++++++++++++++++ 7 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json create mode 100644 common/changes/@rushstack/rush-client-core/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json create mode 100644 common/changes/@rushstack/rush-daemon/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json diff --git a/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json b/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json new file mode 100644 index 00000000000..767d5081793 --- /dev/null +++ b/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Include the bundled implementation in the daemon's runtime fingerprint and avoid resolving every file's real path on each request.", + "type": "patch" + } + ], + "packageName": "@microsoft/rush" +} diff --git a/common/changes/@rushstack/rush-cli-client/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json b/common/changes/@rushstack/rush-cli-client/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json new file mode 100644 index 00000000000..054f90cf9d9 --- /dev/null +++ b/common/changes/@rushstack/rush-cli-client/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-cli-client", + "comment": "Start rush-client without loading the Rush engine, which removes about 1.5 seconds from every invocation on Windows.", + "type": "patch" + } + ], + "packageName": "@rushstack/rush-cli-client" +} diff --git a/common/changes/@rushstack/rush-client-core/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json b/common/changes/@rushstack/rush-client-core/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json new file mode 100644 index 00000000000..da964e57177 --- /dev/null +++ b/common/changes/@rushstack/rush-client-core/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-client-core", + "comment": "Keep waiting for a slow detached daemon start after the requesting client gives up, so later clients can use the daemon instead of finding an abandoned startup reservation.", + "type": "patch" + } + ], + "packageName": "@rushstack/rush-client-core" +} diff --git a/common/changes/@rushstack/rush-daemon/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json b/common/changes/@rushstack/rush-daemon/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json new file mode 100644 index 00000000000..13b7809aac7 --- /dev/null +++ b/common/changes/@rushstack/rush-daemon/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-daemon", + "comment": "Move the daemon start command and launcher error into a module that does not load the Rush engine.", + "type": "none" + } + ], + "packageName": "@rushstack/rush-daemon" +} \ No newline at end of file diff --git a/libraries/rush-client-core/README.md b/libraries/rush-client-core/README.md index 6c1afe75e68..9341bb96d37 100644 --- a/libraries/rush-client-core/README.md +++ b/libraries/rush-client-core/README.md @@ -46,7 +46,10 @@ file sharing on Windows. The winning client rechecks readiness, reclaims only an absent/dead owner, and reserves `.starting` before handing the explicit command to a detached startup helper. The helper spawns without a shell and retains that reservation until the daemon completes hello/ping readiness, -independently of whether the requesting client survives. Clients still await +independently of whether the requesting client survives. It waits for a live launcher for +at least 120 seconds, even when the requesting client's own deadline is shorter, so a slow +first start (for example while Windows scans newly installed files) is still handed off to +later clients instead of leaving an abandoned reservation. Clients still await hello/pong under bounded backoff. Stdout/stderr go to `.log`. No PID is killed; a live (possibly reused) PID with an unreachable socket fails closed. The helper uses a stable tool cwd, and the starting client awaits its exit after diff --git a/libraries/rush-client-core/src/connectOrStartDaemon.ts b/libraries/rush-client-core/src/connectOrStartDaemon.ts index 0889608be28..f2c077d5c7a 100644 --- a/libraries/rush-client-core/src/connectOrStartDaemon.ts +++ b/libraries/rush-client-core/src/connectOrStartDaemon.ts @@ -34,6 +34,13 @@ interface IStartupHelper { readonly closed: Promise; } +/** + * The minimum time the detached helper waits for a live launcher to become ready. It is independent of the + * requesting client's deadline: a slow first start (for example while Windows scans newly installed files) + * would otherwise leave a retained reservation that keeps every later client from using the ready daemon. + */ +const STARTUP_HELPER_READINESS_TIMEOUT_MS: number = 120_000; + /** A version-selected launch command supplied by the embedding application, never guessed by the core. @beta */ export interface IDaemonStartCommand { readonly command: string; @@ -477,7 +484,7 @@ async function spawnDetachedAsync( paths: options.paths, startCommand: start, token, - timeoutMs: Math.max(1, deadline - Date.now()) + timeoutMs: Math.max(STARTUP_HELPER_READINESS_TIMEOUT_MS, deadline - Date.now()) }; let delivered: boolean = false; try { diff --git a/libraries/rush-client-core/src/test/connectOrStartDaemon.test.ts b/libraries/rush-client-core/src/test/connectOrStartDaemon.test.ts index df7fc390cd5..b61affb228e 100644 --- a/libraries/rush-client-core/src/test/connectOrStartDaemon.test.ts +++ b/libraries/rush-client-core/src/test/connectOrStartDaemon.test.ts @@ -147,6 +147,25 @@ describe('detached daemon startup', () => { expect(fs.existsSync(getDaemonStartupFilePath(paths))).toBe(false); }, 15000); + it('lets the detached helper finish a startup that outlasts the requesting client', async () => { + fs.writeFileSync(path.join(folder, 'hold-prebind'), ''); + const first = startClient({ ...options, startupTimeoutMs: 1000 }); + const barrier: string = path.join(folder, 'prebind'); + const deadline: number = Date.now() + 5000; + while (!fs.existsSync(barrier) && Date.now() < deadline) await delayAsync(20); + const daemonPid: number = Number(fs.readFileSync(barrier, 'utf8')); + expect((await first.result).code).not.toBe(0); + expect(fs.existsSync(getDaemonStartupFilePath(paths))).toBe(true); + + // The launcher becomes ready only after the first client has given up. + fs.unlinkSync(path.join(folder, 'hold-prebind')); + const client = await connectOrStartDaemonAsync(options); + expect((await client.status).pid).toBe(daemonPid); + await client.closeAsync(); + expect(fs.readFileSync(path.join(folder, 'starts'), 'utf8')).toBe(`${daemonPid}\n`); + expect(fs.existsSync(getDaemonStartupFilePath(paths))).toBe(false); + }, 15000); + it('preserves an unresolved startup reservation rather than trusting or reclaiming its contents', async () => { const startupPath: string = getDaemonStartupFilePath(paths); const contents: string = JSON.stringify({ pid: process.pid, startedAt: 'not an ownership contract' }); From a27762209fd00957977f82ac696ba4e46ed4d17a Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 22:20:12 +0000 Subject: [PATCH 08/84] rush-lib: resolve rig profiles asynchronously in the daemon project loader The heft-config-file rig fallback resolves the rig profile synchronously, which serializes the daemon's concurrent uncached project-configuration loads (two per request). Resolve it asynchronously first; RigConfig caches the same result, and a resolution failure still surfaces through a fresh instance exactly as before. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- ...ient-startup-perf_2026-09-23-22-20-00.json | 4 +- .../src/api/RushProjectConfiguration.ts | 18 ++++- .../api/test/RushProjectConfiguration.test.ts | 71 +++++++++++++++++++ 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json b/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json index 767d5081793..ed7c2b1852f 100644 --- a/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json +++ b/common/changes/@microsoft/rush/thelarkinn-client-startup-perf_2026-09-23-22-20-00.json @@ -2,9 +2,9 @@ "changes": [ { "packageName": "@microsoft/rush", - "comment": "Include the bundled implementation in the daemon's runtime fingerprint and avoid resolving every file's real path on each request.", + "comment": "Speed up daemon requests: include the bundled implementation in the runtime fingerprint without resolving every file's real path on each request, and resolve rig profiles asynchronously when the daemon reloads project configuration.", "type": "patch" } ], "packageName": "@microsoft/rush" -} +} \ No newline at end of file diff --git a/libraries/rush-lib/src/api/RushProjectConfiguration.ts b/libraries/rush-lib/src/api/RushProjectConfiguration.ts index 9a621e544ac..d3e356756b2 100644 --- a/libraries/rush-lib/src/api/RushProjectConfiguration.ts +++ b/libraries/rush-lib/src/api/RushProjectConfiguration.ts @@ -6,7 +6,7 @@ import * as path from 'node:path'; import { AlreadyReportedError, Async, FileSystem, JsonFile, Path } from '@rushstack/node-core-library'; import type { ITerminal } from '@rushstack/terminal'; import { ProjectConfigurationFile, InheritanceType } from '@rushstack/heft-config-file'; -import { RigConfig, type IRigConfigJson } from '@rushstack/rig-package'; +import { RigConfig, type IRigConfigJson, type ILoadForProjectFolderOptions } from '@rushstack/rig-package'; import type { RushConfigurationProject } from './RushConfigurationProject'; import { RushConstants } from '../logic/RushConstants'; @@ -712,10 +712,22 @@ async function loadIsolatedRigConfigAsync(projectFolder: string): Promise { + describe(RushProjectConfiguration._tryLoadForProjectsUncachedAsync.name, () => { + let folder: string; + const write = (relativePath: string, json: object): void => { + const filename: string = path.join(folder, relativePath); + fs.mkdirSync(path.dirname(filename), { recursive: true }); + fs.writeFileSync(filename, JSON.stringify(json)); + }; + const project = (name: string): RushConfigurationProject => + ({ + packageName: name, + projectFolder: path.join(folder, name), + projectRelativeFolder: name + }) as RushConfigurationProject; + const loadAsync = ( + ...projects: RushConfigurationProject[] + ): Promise> => + RushProjectConfiguration._tryLoadForProjectsUncachedAsync( + projects, + new Terminal(new StringBufferTerminalProvider()) + ); + const getOutputFolderNames = ( + configuration: RushProjectConfiguration | undefined + ): string[] | undefined => { + const outputFolderNames: ReadonlyArray | undefined = + configuration?.operationSettingsByOperationName.get('_phase:build')?.outputFolderNames; + return outputFolderNames && [...outputFolderNames]; + }; + + beforeEach(() => { + folder = fs.mkdtempSync(path.join(os.tmpdir(), 'rush-project-rigs-')); + for (const name of ['rigged', 'missing-profile', 'own-file']) { + write(`${name}/package.json`, { name, version: '1.0.0' }); + } + write('rigged/node_modules/example-rig/package.json', { name: 'example-rig', version: '1.0.0' }); + write('rigged/node_modules/example-rig/profiles/default/config/rush-project.json', { + operationSettings: [{ operationName: '_phase:build', outputFolderNames: ['from-rig'] }] + }); + write('rigged/config/rig.json', { rigPackageName: 'example-rig' }); + for (const name of ['missing-profile', 'own-file']) { + write(`${name}/node_modules/example-rig/package.json`, { name: 'example-rig', version: '1.0.0' }); + write(`${name}/config/rig.json`, { rigPackageName: 'example-rig', rigProfile: 'missing' }); + } + write('own-file/config/rush-project.json', { + operationSettings: [{ operationName: '_phase:build', outputFolderNames: ['from-project'] }] + }); + }); + + afterEach(() => { + fs.rmSync(folder, { recursive: true, force: true }); + }); + + it('loads configuration from a rig profile', async () => { + const rigged: RushConfigurationProject = project('rigged'); + const configurations = await loadAsync(rigged); + expect(getOutputFolderNames(configurations.get(rigged))).toEqual(['from-rig']); + }); + + it('reports a missing rig profile only when the rig provides the configuration', async () => { + await expect(loadAsync(project('missing-profile'))).rejects.toThrow( + 'The rig profile "missing" is not defined by the rig package "example-rig"' + ); + const ownFile: RushConfigurationProject = project('own-file'); + const configurations = await loadAsync(ownFile); + expect(getOutputFolderNames(configurations.get(ownFile))).toEqual(['from-project']); + }); + }); + describe('operationSettingsByOperationName', () => { it('loads a rush-project.json config that extends another config file', async () => { const rushProjectConfiguration: RushProjectConfiguration | undefined = From 3b948f2b5ba2cb0cab11fcfd7ee990d4b6c47c9d Mon Sep 17 00:00:00 2001 From: selarkin Date: Wed, 23 Sep 2026 22:27:12 +0000 Subject: [PATCH 09/84] docs: describe the first daemon start from a refreshed Windows snapshot Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/rush/dogfooding-rush-daemon.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/rush/dogfooding-rush-daemon.md b/docs/rush/dogfooding-rush-daemon.md index 58a549ee197..70c1fde31a3 100644 --- a/docs/rush/dogfooding-rush-daemon.md +++ b/docs/rush/dogfooding-rush-daemon.md @@ -151,6 +151,11 @@ node common/scripts/install-run-rush.js build --to @rushstack/rush-cli-client node common/scripts/install-run-rush.js deploy --scenario rush-daemon-dogfood --target-folder common/temp/rush-daemon-dogfood --overwrite ``` +On Windows, the first daemon start from a new or refreshed snapshot can take longer than the client's 15-second +startup deadline while Windows scans the newly written files. That request then uses native Rush (with a +`rush-client: ... Using in-process Rush.` message), but the daemon finishes starting in the background and the next +request uses it. + The daemon's identity is the canonical repository root plus the selected Rush version, so each checkout or worktree has its own daemon, and `rush-client daemon ...` commands address the daemon for the checkout that contains the current directory. From 8c902eec98607f87fc8e4ccb1095f93bf49d165a Mon Sep 17 00:00:00 2001 From: Sean Larkin Date: Wed, 30 Sep 2026 04:54:26 -0700 Subject: [PATCH 10/84] [rush] rushd: fix the bugs found by dogfooding the Rush daemon (stacked on #6046) (#6102) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [rush-lib] Workspace fingerprint: ignore build state of projects nested under common/config (and 3 more) Swarm integration step 1; original commit 433c87f24f (merge of swarm/r05 at 190d77ea17). Commits folded into this step (4): - 40b5b24d72 [rush-lib] Workspace fingerprint: ignore build state of projects nested under common/config - 1880e7b2b7 [rush-daemon] Warm set: keep resource-free retained results under memory pressure and idle expiry - 07e908d32d [rush-daemon] Admission: don't spend a default wait budget behind a graph load or across an admitted request's execution - 190d77ea17 [rush-lib] Move the nested common/config fingerprint test to the end of its suite Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Warm set: read status once per maintenance pass, not once per project (and 1 more) Swarm integration step 2; original commit 8553b2be55 (merge of swarm/r07 at d94054e04d). Commits folded into this step (2): - 57dd6a4b45 [rush-daemon] Warm set: read status once per maintenance pass, not once per project - d94054e04d [rush-lib] Share rig profile loads across projects in the uncached rush-project.json load Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Keep RUSH_DAEMON* variables out of lifecycle script environments (and 3 more) Swarm integration step 3; original commit 3b2d24277d (merge of swarm/r06 at 6c10e241a4). Commits folded into this step (4): - 803345396b [rush-lib] Keep RUSH_DAEMON* variables out of lifecycle script environments - b49d8b10cb [rush-daemon] Keep one daemon across per-session environment differences - 36892176cb [rush-daemon-transport] Reap detached operation groups of a daemon that died uncleanly - 6c10e241a4 [rush-daemon] Run each operation with its requester's per-session environment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Let daemon engines serve builds with declared daemon-compatible plugins (and 1 more) Swarm integration step 4; original commit 1e786a4252 (merge of swarm/r01 at 162ff05eaf). Commits folded into this step (2): - 5fe505bf1a [rush-lib] Let daemon engines serve builds with declared daemon-compatible plugins - 162ff05eaf [rush-lib] Walk each plugin package once in the daemon runtime fingerprint Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Abort a shared iteration's leftover work once no remaining client needs it Swarm integration step 5; original commit 02f83a99ae (merge of swarm/r05 at bd7ac88caa). Refs: board 207. Commits folded into this step (1): - bd7ac88caa [rush-daemon] Abort a shared iteration's leftover work once no remaining client needs it (board 207) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Run the initial script for every command outside watch mode Swarm integration step 6; original commit 519ae33c8d. Refs: task 50, board 115. ShellOperationRunner runs a phase's `:incremental` script whenever the operation has a last state. Native `rush build` executes one iteration, so only watch mode ever had one, and watch mode turns cache writes off. rushd keeps one operation graph across requests, so from its second request on a daemon `build` ran `_phase:build:incremental` (in odsp-web, `heft run --only build --` without --clean) with cache writes on. Outputs of a deleted or renamed source file stayed in lib/ and were cached under the key native Rush computes for the clean build, so later native builds restored them (t04 board 115, board 450). ShellOperationRunnerPlugin now sets the incremental command only in watch mode. Every other command runs the initial script, exactly as a single `rush build` does, which is option (a) of task 50. Native `rush build` is unchanged (it never has a last state) and watch mode keeps `:incremental` with writes off. Tests: a rush-lib unit test runs a repeated operation with and without watch mode, and a daemon integration test runs three warm builds of a project with an `:incremental` script. Before the fix the second daemon build logged "Invoking (incremental): node build.cjs --incremental" followed by "Successfully set cache entry". (cherry picked from commit e382d2ac80f881451b7d527ea9a97e3037880923) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Share input captures between concurrent requests Swarm integration step 7; original commit f7ac88dc9d (merge of swarm/r07 at 55a16f14ef). Refs: F1 coalescer, board 555. GATE OK ch01 board 930 (tree 41b2227d59). CONFIRMED by ch03 board 681, t06 board 701/board 749, t01 board 861. Only 55a16f14ef; b6692d09a5 (task 74) is not gated. Commits folded into this step (1): - 55a16f14ef [rush-daemon] Share input captures between concurrent requests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Skip cache writes when input files change while the inputs snapshot is taken Swarm integration step 8; original commit bc03023091 (merge of swarm/r06 at 29f86b181c). Refs: task 77. ch01 GATE OK board 1019 (tree 80c9659bac); claim board 698, CONFIRMED by o04 board 738, o03 board 742, o05 board 748. Commits folded into this step (2): - a644819daf [rush-lib] Don't write a build cache entry for inputs saved while the snapshot was taken - 29f86b181c [rush-lib] Make the parameter identity test independent of the host's RUSH_PARALLELISM Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] don't let a request received during a batch's reconcile join that batch Swarm integration step 9; original commit d0ed461613 (merge of swarm/r05 at 199c69347c). Refs: board 371, task 72. ch01 GATE OK board 1152 (gated tree 5c2b6b6cbf). Commits folded into this step (1): - 199c69347c [rush-daemon] Don't let a request received during a batch's reconcile join that batch (board 371) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Another request's graph load doesn't spend any finite wait timeout Swarm integration step 10; original commit 908dcc1f27 (merge of swarm/r05 at 868e4b9299). Refs: task 7 part 3, board 891. ch01 GATE OK board 1196 (gated tree 55698be825). Commits folded into this step (1): - 868e4b9299 [rush-daemon] Admission: another request's graph load doesn't spend any finite wait timeout Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Keep cache trust for unselected retained results and re-verify untrusted ones Swarm integration step 11; original commit 4456f17460 (merge of swarm/r07 at 53654af37c). Refs: board 401, task 74, board 982, board 1024. ch01 GATE OK board 1197 (gated tree 3014a03bb8). Commits folded into this step (3): - b6692d09a5 [rush-lib] Keep cache trust for unselected retained results and re-verify untrusted ones - 492126a051 [rush-lib] Re-verify retained results built against unverified dependency outputs without the build cache - 53654af37c [rush-daemon] Test that input captures are shared only between equal fingerprint environments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Show engine and plugin warnings in agent output; keep the daemon alive when git hash-object fails Swarm integration step 12; original commit cc5c6aab7e (merge of swarm/r01 at 575cbf5698). Refs: board 601, task 80, board 899. ch01 GATE OK board 1272 and board 1273 (gated tree b691997c35). Commits folded into this step (3): - c11ee10320 [rush-cli-client] Show engine and plugin warnings in agent output - c77aa1dbef [package-deps-hash] Handle a git hash-object failure while ls-files and status still run - 575cbf5698 [package-deps-hash] Name the git command that failed in the error message Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [node-core-library] LockFile ties, TZ/locale rule, /proc start times and own-start memo Swarm integration step 13; original commit 0347cd0dbb. Refs: tasks 67, D and 62. ch01 GATE OK board 1389; o05 board 1360 (task 62 on ch01-t62: 32/32 served in 3 of 3 bursts); t01 board 1106/board 1186. Commits folded into this step (6): - 3dfb75ec86 [node-core-library] Don't grant a LockFile twice when lockfile birthtimes tie - 074f207217 [node-core-library] Keep the LockFile of a running process with another time zone or locale - 4e43a1ef8f [node-core-library] Don't set dirtyWhenAcquired when the holder releases the lock during a check - ad9c1211b4 [node-core-library] Read the start times of other LockFile processes from /proc on Linux - 698af4ef92 [node-core-library] Run ps for the start time of the current LockFile process only once on Linux - 9249e85d63 [rush-client-core] Test clients that start the daemon at the same time Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A request can share an input capture that started after it was received Swarm integration step 14; original commit f2ec9ffc26. ch01 GATE OK board 1430 (tree ae49a66e47 with o07's stack); t06 CONFIRMED board 1365; o05 board 1412 (cold split gone in auto-start mode). Commits folded into this step (1): - a4de3fe5fe [rush-daemon] Share running input captures that started after a request was received Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Hand requests to in-process Rush when a project's configuration cannot load Swarm integration step 15; original commit 69f30e84ab (merge of swarm/r01-t70 at c1b5c95adb). Scope: task 70. ch01 GATE OK board 1522 (tree 3a564b4533; WorkspaceRequestLifecycle.ts resolved with r07's board 1231 recipe: the Reuse capture stays tolerant and keeps task 94's receipt); o01 board 1291; r01 board 1250. Commits folded into this step (5): - 08aed7efd2 [package-deps-hash] Handle a git hash-object failure while ls-files and status still run - 3d786c1f78 [package-deps-hash] Name the git command that failed in the error message - a11aa6b774 [rush-lib] Name the project whose configuration an engine cannot load - 24cf595d6a [rush-lib] Report a missing project dependency file to an engine's host - c1b5c95adb [rush-daemon] Hand requests to in-process Rush when a project's configuration cannot load Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] With cache writes off, a result whose inputs changed during the run is not trusted as up to date Swarm integration step 16; original commit d6ac7c43f3 (merge of swarm/r06-t92 at 72c2f0e4f8). Scope: task 92. ch01 GATE OK board 1563 (tree 5c0b0ac25d); r06 board 1270; CONFIRMED by t03, t04, ch05 and o03. Commits folded into this step (1): - 72c2f0e4f8 [rush-lib] Re-run operations whose inputs changed during the iteration, also with cache writes off Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] A failed pnpm install releases the package-manager lock Swarm integration step 17; original commit 9a58fc2f85 (merge of swarm/r01-t103 at 93eacefb71). Scope: task 103. ch01 GATE OK board 1565 (tree e234c8d86d); o04 CONFIRMED board 1477; r01 board 1440. This is upstream row 16's commit. Commits folded into this step (1): - 93eacefb71 [rush-lib] Release the package manager lock when installing the package manager fails Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] rush-lib path handoff, startup reservation, lost-connection message and a private runtime folder Swarm integration step 18; original commit 261e4320dd (merge of swarm/r03-t87 at d9366621c8). Scope: tasks 4+5, 68, 86 and 87. ch01 GATE OK board 1675 (tree f85ce62f88). Commits: bb1d272738 (tasks 4+5), 959cbb84ee (task 68), a68e4d96a5, 696dba1d3e (task 86) and d9366621c8 (task 87). Commits folded into this step (5): - bb1d272738 [rush-lib] Hand off a by-name resolvable rush-lib and load built-in cache plugins from deploy outputs - 959cbb84ee [rush-client-core] Resolve a retained startup reservation next to a ready daemon - a68e4d96a5 [rush-client-core] Resolve a remaining startup reservation before a plain daemon stop - 696dba1d3e [rush-client-core] Explain a daemon that exited before delivering a result - d9366621c8 [rush-daemon] Meet one daemon per checkout whatever TMPDIR or XDG_RUNTIME_DIR says (task 87) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Wait for a daemon a live process is still starting instead of running Rush in-process Swarm integration step 19; original commit a81a00520d (merge of o07/startup-fallback at 668753ad4d). Scope: task 95. Brings 647a8bf902 [rush-client-core] and 668753ad4d [rush-cli-client]. Gate: ch01 GATE OK board 1731 (tree e4a00622d4); burst: t01 board 1716. Commits folded into this step (2): - 647a8bf902 [rush-client-core] Wait for a daemon that a live process is still starting before permitting in-process Rush - 668753ad4d [rush-cli-client] Don't run Rush in-process while daemon startup is still pending Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Reuse workspace input digests and project configurations while their files are unchanged Swarm integration step 20; original commit 0f987a5b62 (merge of swarm/r07-t93-on-s10 at 18900fe380). Scope: task 93. Brings 143f21a891 and 18900fe380 [rush-lib]. Gate: ch01 GATE OK board 1869 (tree 8892a71708); m01 CONFIRMED board 1738 and board 1818; t04 board 1794. Commits folded into this step (2): - 143f21a891 [rush-lib] Reuse workspace input digests and project configurations while their files are unchanged - 18900fe380 [rush-lib] Test that a project configuration that failed to load is loaded afresh once it is fixed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] The daemon's admission and restart-drain reasons in full in agent output, and rushd's startup wait as a pipe line Swarm integration step 21; original commit 478f4d3af0 (merge of swarm/r04-t83-a81 at 4d1a045fca). Scope: task 83. Brings task 83's chain as resolved onto a81a00520d: 244ed8b1f5 (merge of swarm/r04-t83-clip @9d71dbe9bb, the clip fix for board 1539) and 4d1a045fca, with board 711 (1886d80936), tasks 79, 71 and 50 and the restart-drain timeout. Gate: ch01 GATE OK board 1872 (lifts board 1778); combined with task 93, tree d0e8df0156, full suite board 1888. Commits folded into this step (21): - 2e7d278d8f [rush-daemon] Report the full log path of failed and warning operations - 3b91eec856 [rush-cli-client] Keep spawned test clients on the default output when tests run in an agent shell - 1ae03fc555 [rush-cli-client] Agent output that holds at repository scale - 862ed5e676 [rush-cli-client] Do not report a daemon shutdown as a user cancellation - 39cae5f22b [rush-cli-client] Send no terminal width for a pseudo-terminal without a size - 5f3c36fe17 [rush-daemon] Describe a rejected request without the debug messages of the graph load (task 71) - 563e541fdf [rush-cli-client] Cap a multi-line error message in agent output (task 71) - 2377740ab9 [rush-daemon] Keep the loaded graph when a request only selects an unknown project (task 71) - e382d2ac80 [rush-lib] Run the initial script for every command outside watch mode (task 50, board 115) - b7bba00fb3 [rush-cli-client] Never leave agent output silent on a pipe (task 79) - feacc54bf7 [rush-daemon] Report a restart drain as a queue position (task 79) - 1886d80936 [rush-cli-client] Report Ctrl+C during graph preparation as cancelled (board 711) - ae31023aad [rush-cli-client] Report an empty selection in agent output (board 634) - bacfa3d5af [rush-daemon] Do not apply the client-default queue timeout to a restart drain (task 83) - 5f1c7b4f1b [rush-cli-client] Keep "daemon admission failed ()" in the agent summary; word an empty selection like native Rush (board 785, board 634) - 3b5dc578d6 [rush-daemon] Document the restart drain and its client-default timeout (task 83) - cbf8a3a45e [rush-daemon] Apply the client-default timeout to a restart drain while a rushx script runs or for later requests (task 83) - 6cd768c6b3 [rush-daemon] Suggest stopping the rushx script when a restart drain times out behind one (task 83) - b2f9d78f1f [rush-daemon] Name the waived time when a restart drain times out (task 83) - 9d71dbe9bb Write the daemon's reason for an admission failure in full in agent output (task 83, board 1539) - 4d1a045fca Write rushd's startup wait as a pipe line in agent output (task 83 on task 95) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Report failed operations as they fail in agent output Swarm integration step 22; original commit 6bc6dd8f83 (merge of swarm/r04-t108-a81 at 14faa4e5be). Scope: task 108. Brings task 108 items 1 and 3 onto task 83: agent output prints a failed operation's first diagnostics as soon as that operation finishes, no `running` line before 10 s for quick requests, and each TypeScript error once (t07 board 1930: L4 17 of 41 on 4d1a045fca against 24 of 41 on s11; 38 of 41 with items 1 and 3 on their old base, board 1692). Gate: ch01 GATE OK board 1980 (tree 9079647277) Commits folded into this step (1): - 14faa4e5be Report failed operations as they fail in agent output (task 108 items 1 and 3) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Keep results that a plugin found up to date in long-lived operation graphs Swarm integration step 23; original commit e16ed01d3a (merge of swarm/r01-t85-a81 at 4066e8f5d7). Scope: task 85. Brings task 85 onto task 108 items 1+3: the daemon keeps a result that a plugin such as fstrace found up to date across requests, and checks a Skipped result again when it was marked unverifiable. m01 CONFIRMED the gated tip (board 2024: 72 of 72 hot requests "1066 no op") and measured 24 of 24 hot B6 requests as "no operations needed" (board 2073); ch04 CONFIRMED 6e39ea6527 (board 2003) and posted the product-default A/B (board 2132: -0.24 s [-0.57, +0.09]). ch01's combined suite on this tree passes (board 2150: rush-lib 1169/1, a timing flake in InstallRunScripts.test that task 85 doesn't touch, 5 of 5 on rerun; rush-daemon 515/0, rush-cli-client 367/0). Gate: ch01 GATE OK board 1955, combined suite board 2150 (tree 1c6a3bfa10) Commits folded into this step (2): - 95c8ece420 [rush-lib] Retain results that a plugin found up to date in long-lived operation graphs - 4066e8f5d7 [rush-lib] Check a Skipped result again if it was marked unverifiable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Pass getOperationEnvironment to the before/after iteration hooks (and 7 more) Swarm integration step 24; original commit 31a8db3e65 (merge of swarm/r01-t85-x106b at 4835a8700e). Scope: tasks 85 and 106. Brings task 106 (guarded :incremental builds in the daemon) and task 84 at de9a503a50 through r01's 85x106 resolution. This is step 1 of the path in board 2046 and board 2161; step 2 brings task 106's test-only tip b767c64dac. Merging b767c64dac straight onto task 85 conflicts in CacheableOperationPluginRetainedResults.test.ts. Gate: ch01 GATE OK board 2161 (tree 8250469c70) Commits folded into this step (8): - c7400b3f9c [rush-lib] Pass getOperationEnvironment to the before/after iteration hooks - e07159a38f [rush-lib] Let the daemon run :incremental scripts when a guard allows it - 90a76234e5 [rush-lib] Register RUSH_DAEMON_INCREMENTAL_BUILDS as a known environment variable - b1647a952a [rush-lib] Test that --only consumers of a retained incremental result skip cache writes - aed31544c2 [rush-lib] Test that a terminated command drops its incremental base - 69e33c03a8 [rush-lib] Format the incremental build changes with Prettier - c3afda3587 [rush-lib] Test that a run whose inputs changed while it ran leaves no incremental base - de9a503a50 [rush-lib] Check dependencies reached through same-project phases in the incremental guard Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Let the daemon run :incremental scripts when a guard allows it Swarm integration step 25; original commit 7d0ffb955b (merge of swarm/r06-t106 at b767c64dac). Scope: task 106. Brings task 106's test-only tip onto step 1 (31a8db3e65). With task 106 the daemon runs an operation's :incremental script, on top of the outputs of its last successful run in the daemon, when only files that the operation builds were edited since then and its output folders are unchanged; anything else runs the initial command. A result built on an incremental base is never written to the build cache. Kill switch: daemon.incrementalBuilds false in rush.json, or RUSH_DAEMON_INCREMENTAL_BUILDS=0. Task 84 passes getOperationEnvironment to the before/after iteration hooks. Second agents: t03 CONFIRMED (board 2067, 37 of 39; TAMPERI is the declared limitation) and t04 CONFIRMED (board 2100). ch01 on this tree (board 2161): rush-lib 1228/0, rush-daemon 516/0, rush-cli-client 367/0; 50 mutants, 42 killed; 33 of 33 end-to-end rows against a native cache-off oracle. Known costs, per ch01's rulings in board 2161: revisiting a state restores less from the cache (Rule 2), and an in-place rewrite of an output file by another writer is not detected (TAMPERI; task 139, r06's 641c39f120 in board 2171). Gate: ch01 GATE OK board 2161 (tree b1c0c2e690) Commits folded into this step (1): - b767c64dac [rush-lib] Test that an incremental result taints cache writes through an operation without a script Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that a configuration is read again while only its rig.json may still be changing Swarm integration step 26; original commit 8a1d1ee8dd (merge of swarm/r07-t93-nit at f5826544d1). Scope: task 93 NIT. Test-only: 7 lines in RushProjectConfiguration.test.ts, no source change. It kills ch01's task 93 mutant M13 (board 1869): with the mutant the new assertion fails, 25/1, and 18900fe380's test file lets it survive, 26/0. Full rush-lib on the cherry-pick onto b1c0c2e690: 1228/0 (board 2178). The deployed daemon files don't change, so snapshot s14 (7d0ffb955b) is unaffected. Gate: ch01 GATE OK board 2178 (tree 09d90522de) Commits folded into this step (1): - f5826544d1 [rush-lib] Test that a configuration is read again while only its rig.json may still be changing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] The incremental guard detects an in-place rewrite of an output file Swarm integration step 27; original commit 6eae8fadad (merge of swarm/r06-t106 at 641c39f120). Scope: task 139. Brings r06's fold-in on task 106 (board 2171), in place of b767c64dac. The output signature of an operation now records each output file's inode, size and mtime as well as the folder listing, so when another writer appends to or rewrites an output file in place, the next build runs the initial command ("its output folders changed since its last successful run") instead of an incremental run on the stale file. This closes t03's TAMPERI. A touch that changes only mtime now also costs one initial run. r06 measured about 4 µs per output file per read. ch01 on tree 1eb87abdd2 (board 2211): rush-lib 1231/0, rush-daemon 516/0, rush-cli-client 367/0; b767c64dac's manifest fails 4 of the new tests; 10 mutants, 6 killed (N1, N3 and N6 survive; task 138 adds their tests); tamper end-to-end rows 9/9 against 2/9 before, full rows 33/33, cache-off rows 7/7. On this branch the tree is 1eb87abdd2 plus r07's test-only 8a1d1ee8dd change, byte for byte. Gate: ch01 GATE OK board 2211 (tree 1eb87abdd2) Commits folded into this step (1): - 641c39f120 [rush-lib] Detect in-place rewrites of output files in the incremental guard Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Tests for the incremental guard's configuration inputs, content-hashed outputs, invalidation and skip record Swarm integration step 28; original commit 0d30a5164c (merge of swarm/r06-t138 at dbd5d99147). Scope: task 138 and t106d. Brings r06's task 138 tests (board 2302) and t106d (4c9f23e650, board 2233). Only 3 test files change (+204/-18): ProductionDaemonRequestResolver.test.ts, IncrementalExecutionGuardPlugin.test.ts and OperationOutputManifest.test.ts. No product code changes, so no snapshot is needed. ch01 on tree 45c2c5edd5 (board 2364): rush-lib 1241/0 (1 skipped); rush-daemon 516/0 with t106d. With the new tests, each of the 10 guard and manifest mutants (G9-G12, G19, G24, L1, N1, N3, N6) fails its intended test; all 10 pass the old tests. t106d's kill-switch test fails against a product mutant that drops the daemon.incrementalBuilds check at PhasedScriptAction.ts:666, and 6eae8fadad's version of it passed that mutant. Gate: ch01 GATE OK board 2364 (tree 45c2c5edd5) Commits folded into this step (2): - 4c9f23e650 [rush-daemon] Make the test of daemon.incrementalBuilds: false edit a source file - dbd5d99147 [rush-lib] Test the incremental guard's configuration inputs, content-hashed outputs, invalidation and skip record Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A served rushx script releases the lifecycle gate once it starts, so reloads don't wait for it (board 113) (and 5 more) Swarm integration step 29; original commit 3857bc9926 (merge of swarm/r05-stack-int at c35a77cc5c). Brings r05's stack (board 1931): - Task 55: a served rushx script releases the lifecycle gate once it starts, so reloads don't wait for it. - Task 7 part 3 text: admission timeouts name the client's wait timeout and the time that isn't counted. - Task 96: a rushx script that arrives while a restart is pending waits for the restart instead of postponing it, and admission failures in legacy output and rushx-client print the daemon's reason. - Task 98: a request spends its wait timeout only while it waits for other requests. - The race fix (c35a77cc5c, restacked from 9370662d5f): a client that follows a daemon restart connects to the successor that the restarting daemon launches instead of racing it. ch01 on tree b500e451d4 (board 2429): build rc 0 with 0 warnings; rush-daemon 544/0, rush-lib 1231/0, rush-cli-client 374/0, rush-client-core 129/0 on its own (2 known flakes in the full run), protocol 166/0, transport 73/0, reporter 519/0, wire-e2e 4/0. The red checks fail as intended without each change. 36 of 43 mutants are killed; the 7 survivors are test gaps and change no claim. The end-to-end rows R55, R96 and R96A pass on the stack and fail on s14b's files. Gate: ch01 GATE OK board 2429 (tree b500e451d4 on 6eae8fadad; 87e0e3a10f together with task 138's merge, in either order) Commits folded into this step (6): - c8cd36f574 [rush-daemon] A served rushx script releases the lifecycle gate once it starts, so reloads don't wait for it (board 113) - 4a5a13bddf [rush-daemon] Name the client's wait timeout and uncounted time in admission timeouts - cafa954eb4 [rush-daemon] A rushx script that arrives while a restart is pending waits for it instead of postponing it (task 96) - 7ba7021ed6 [rush-cli-client] Admission failures in legacy output and rushx-client print the daemon's reason (task 96) - 4ddb1fdbfe [rush-daemon] Spend a request's wait timeout only while it waits for other requests (task 98) - c35a77cc5c [rush-client-core] A client that follows a daemon restart connects to the successor the restarting daemon launches instead of racing it (task 96) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A warm-set pass takes the repository lock only when it has work Swarm integration step 30; original commit a4de651852 (merge of swarm/r07-t76 at 6b32aa340e). Scope: task 76. Brings r07's task 76 (board 1730; o05 CONFIRMED board 1904). A warm-set maintenance pass takes the native repository lease only when it has something to evict, a watcher change to apply, or a failed observation to retry. An idle daemon no longer takes common/temp/rush#.lock every 30 s, and a native command that holds the lock no longer puts the warm set in 'native-busy'. Two commits: 40a9f8f5eb (product) and 6b32aa340e (tests for the two lease exemptions a pass relies on). ch01 on tree fe0b6a452a (board 2511): rush-daemon 549/0 (544 + 5 new tests). The old WorkspaceWarmSet.ts fails exactly the 5 new tests. 15 of 16 WorkspaceWarmSet.ts mutants are killed; B6 (no protected check in #shouldEvict) survives and is a test NIT. E2E on ch01's own daemons: 0 lock holds in 150 s idle (5 before), 0 daemon lock attempts while native holds the lock 40 s (12 before, with 'native-busy'). Gate: ch01 GATE OK board 2511 (tree fe0b6a452a) Commits folded into this step (2): - 40a9f8f5eb [rush-daemon] Warm set: take the repository lock only for a maintenance pass that has work - 6b32aa340e [rush-daemon] Warm set: test the two lease exemptions a pass relies on Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [node-core-library] A leftover LockFile whose pid now belongs to an older process is deleted Swarm integration step 31; original commit 02087b3f11 (merge of o07/lockfile-tzrule at d5e74b1813). Scope: task 121. Brings o07's task 121 (board 1815, re-tipped in board 1949; t05 CONFIRMED board 1923 on faa7675fa6). A rush#.lock whose pid has been reused by a live process that started before the file was written no longer blocks every command until that process exits. The lock file keeps its holder only if the recorded start time matches the live process's start time in some real time zone, and on Linux that start time is read from /proc instead of spawning ps. Four commits: 595f8f63c7 (the time zone rule), faa7675fa6 (/proc start time for another time zone or locale), 315e1eae81 (ch04's test for a process that started after the file) and d5e74b1813 (t05's note that a clock change can make a start time fail the rule). ch01 on tree 73696a280a (board 2566): build rc 0 with 0 warnings, rush-daemon 549/0; on b10e8c53e0 node-core-library 329/0 (318 + 11 new tests) and rush-client-core 129/0. The new tests fail 7 times on the old LockFile.ts. 21 LockFile.ts mutants, 16 killed; the 5 survivors are 4 test NITs and one equivalent. E2E with 12 live holders in other time zones and locales: all kept, with 0 ps calls after the first attempt (1.2-7 ms per attempt, against 410-520 ms before); the stale files (pid 1 at +7 min, pid 1 with a 2024 start time, a predating process at +3 min) are now deleted. Gate: ch01 GATE OK board 2566 (tree 73696a280a) Commits folded into this step (4): - 595f8f63c7 [node-core-library] Keep a mismatched LockFile only if its start time is in some time zone - faa7675fa6 [node-core-library] Read the start time from /proc for a LockFile with another time zone or locale - 315e1eae81 [node-core-library] Test a LockFile whose PID started after the file, with a start time in another time zone - d5e74b1813 [node-core-library] Note that a clock change can make a LockFile's start time fail the time zone check Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [credential-cache] A credentials.json that isn't valid JSON no longer prints the stored SAS Swarm integration step 32; original commit 0bef326909 (merge of swarm/r03-t143 at 92cbe2336e). Scope: task 143. Brings r03's task 143 (board 2316; t07 CONFIRMED board 2443 and board 2445). CredentialCache no longer rethrows JsonFile's parse error, which quoted jju's excerpt of the failing line, and in credentials.json that line holds the credential. The error now gives only the line and column and says the contents aren't shown because the file stores credentials. A missing file still gives undefined, other read errors are still wrapped as before, and schema errors are unchanged. The branch carries 08e26894e7 (task 101 part 1: compile the credentials file schema once) through r03's merge eae2fc9ad3, so tasks 101 and 143 merge in either order. ch01 on 2c425fc4f5 (board 2576): build rc 0 with 0 warnings, credential-cache 34/0, azure plugin 10/0, http plugin 14/0, amazon-s3 plugin 52/0. The new tests fail exactly the three torn-file tests on eae2fc9ad3. 12 CredentialCache.ts mutants, 9 killed; the survivors are one equivalent and one test NIT (a read error other than ENOENT). E2E with a marker in the SAS: on the old engine a torn file printed the marker in the daemon, --no-daemon and update-cloud-credentials paths; now it is printed 0 times, and 11 of 20 outputs are byte-identical. Task 142 must land with or after this merge. Gate: ch01 GATE OK board 2576 (tree 692db7d548 after task 121) Commits folded into this step (2): - 08e26894e7 [credential-cache] Compile the credentials file schema once per process - 92cbe2336e [credential-cache] Don't quote the credentials file in its parse error (task 143) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A failed agent-output build returns as soon as nothing unfinished can change its result Swarm integration step 33; original commit 1b51e7af08 (merge of swarm/r04-t108-int at c76de10302). Scope: task 108 a. Brings r04's task 108 item 2, "108(a)": 129e04f8bb on the nits 4bad455570 and item 2 2e066a7758, re-merged onto 3857bc9926 as c76de10302 (board 2133, board 2533). With agent output, a failed `build` returns as soon as nothing unfinished can change its result. Its summary line ends `· independent operations continue in rushd`, and those operations finish in the daemon. The next build waits for them (`queued behind another request`) and doesn't run them again; the daemon's own install and rebuild stop the leftover, and a request that falls back in-process stops it first. Until the leftover ends, a native `rush install` or `rush-client --no-daemon` fails with "Another Rush command is already running in this repository." (o01 board 2598, board 2612), and `daemon status` doesn't count it yet (r04's output follow-up, board 2133, board 2652). Legacy output keeps the old behavior. o02 CONFIRMED item 2 with one row refuted (board 2055), which 129e04f8bb addresses; CONFIRMED by t07 (board 2183) and t05 (board 2193). ch01 on 140b42ad50 (board 2678): build rc 0 with 0 warnings, rush-daemon 573/0, rush-cli-client 383/0, rush-client-core 129/0, rush-daemon-protocol 172/0. 31 new and changed tests fail on the old product. 27 mutants, 22 killed; F4 and Y4 are equivalent, and three are test NITs (the stop ignores the client leaving, the offer ignores a record that is already aborted, and silent operations count as continuing). E2E: a failed build returns in 0.3 s where the base takes 15.3 s, and its independent operation finishes 15.0 s later. Gate: ch01 GATE OK board 2678 (tree 140b42ad50) Commits folded into this step (3): - 2e066a7758 Return a failed build's result early in agent output (task 108 item 2) - 4bad455570 Name a failure without output sooner, and pluralize failures, in agent output (task 108 nits) - 129e04f8bb Stop continuing work before an in-process fallback (task 108, coord board 1981 a) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A request that meets a removed or changed installation waits for the old daemon, then restarts it and runs Swarm integration step 34; original commit c0e17d82ab (merge of swarm/r03-t91f at 503c9e6589). Scope: tasks 125, 91 and 116. Brings r03's task 125 (E1 and E2 of t05 board 1726) with task 91 (a note restarts the 25 s status timer) and task 116 (long socket paths, board 1476), re-tipped onto integration 1b51e7af08 with ch01's resolution of the 108(a) conflicts in 2 files and one test mock line (board 2697, board 2732). When the daemon's installation is removed or changes, a request waits for the running requests to drain, the daemon restarts and the request runs, and the wait line names the installation as the reason; `--wait-timeout` and `--no-wait` fail with that reason. CONFIRMED by t05 on r03-t91d (board 2154, board 2197: the daemon side of cold E2 is 19 of 19 rc 0 where s13 fails 3 of 3). r05 reviewed the drain part of the re-tip (board 2680). ch01 on d9f459d151 (board 2756): build rc 0 with 0 warnings, rush-daemon 590/0, rush-cli-client 398/0, rush-client-core 136/0, rush-daemon-protocol 200/0, rush-daemon-transport 81/0. 33 new tests fail on the old product and 4 suites can't load. 14 mutants, 8 killed; the 6 survivors are test NITs. E2E: with the installation renamed away mid-build, the next build is rc 0 in 21.4 s where the base fails at once with `Cannot find module '@microsoft/rush-lib'`. r03's own run (board 2760): rush-cli-client 398/0, rush-daemon 590/0. Gate: ch01 GATE OK board 2756 (tree d9f459d151) Commits folded into this step (4): - 29b5678225 [rush-daemon] Refuse a socket path too long to reach, and keep older daemons in their folder (task 116, board 1476) - 08ed06de32 [rush-daemon] Restart a daemon whose installation was removed or replaced (task 91) - 1393c18030 [rush-daemon] Answer requests with the installation restart once the running requests finish (task 125) - c2218d0792 [rush-daemon] Wait for an installation restart in the restart drain, like a restart for an environment (task 125, aligned with task 83) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A daemon-served build logs one native telemetry entry per request, and the daemon keeps serving when its own PATH repeats an entry Swarm integration step 35; original commit d87702c813 (merge of swarm/r02-int at c9d60eb5a4). Scope: task 49 and the PATH fix. Brings r02's task 49 (daemon-served builds emitted no Rush command telemetry, board 61): a long-lived engine logs one native telemetry entry per served build request, with the request id, and an iteration's requests are reported before the next iteration starts. Engine disposal waits at most 2 seconds for telemetry uploads (8eccf3a6f1, CONFIRMED by ch05 board 1987). The daemon keeps serving after a plugin adds names to process.env (80704de4e3), and keeps serving when its own startup PATH repeats an entry, where it used to turn away every build (202412e57b, board 2657, CONFIRMED by o05 board 2671; the blobs carry over byte for byte, board 2776). Re-tipped onto integration c0e17d82ab with ch01's order for the 125 conflict in WorkspaceRequestLifecycle.ts (board 2756, board 2774); 108(a)'s 'running' early result counts as early in the telemetry entry. ch01 on b8b1dc6e2f (board 2795): build rc 0 with 0 warnings, rush-daemon 607/0, rush-lib 1249/0, rush-cli-client 398/0, rush-client-core 136/0, rush-daemon-protocol 200/0, rush-daemon-transport 81/0. Reverting the PATH fix or the 2 s flush wait each fails its test; 3 of 5 mutants killed, and the 2 survivors are test NITs for s16. E2E: the early row logs earlyResult true, and a PATH that repeats an entry keeps the same daemon where the old product falls back. r02's own run on c9d60eb5a4 (board 2777) gives the same counts. ch01's finding that ODSP_TELEMETRY_TAG restarts the daemon is pre-existing and is task 168. Gate: ch01 GATE OK board 2795 (tree b8b1dc6e2f) Commits folded into this step (10): - 490b3d94f9 [rush-lib] Extract the phased telemetry entry builder from OperationGraph - 251d987b06 [rush-lib] Let a long-lived engine log one native telemetry entry per request - 6f62ff03f2 [rush-daemon] Log one native telemetry entry per served build request - eb0fd9090e Add change files for per-request daemon telemetry - 80704de4e3 [rush-daemon] Keep serving after a plugin adds names to process.env - 6a17f4bbdc [rush-daemon] Test that an iteration's requests are reported before the next iteration starts - e7b848d6a7 [rush-daemon] Add the request id to each served request's telemetry entry - 8eccf3a6f1 [rush-lib] Wait at most 2 seconds for telemetry uploads when an engine is disposed - ddff7a24ff [rush-daemon] Move the native engine test fixture into its own module - 202412e57b [rush-daemon] Keep serving when the daemon's own PATH repeats an entry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A stuck request no longer keeps a stopped daemon alive Swarm integration step 36; original commit 372742afe7 (merge of swarm/r01-t147-int at a40a901822). Scope: tasks 115 and 147. Brings r01's task 115 (`daemon stop` and the first SIGTERM never ended a daemon whose request was stuck in an await that ignores cancellation) and task 147 (after a clean stop, a ref'd timer or handle kept the process alive after it released its socket and pid.json; t05 board 2199, o04 board 2269), re-tipped onto integration (board 2779, board 2793). The daemon now shuts down within a 10 s deadline, returns a typed result to the running request and releases its socket, lockfile and repository lock. CONFIRMED: 115 by ch05 board 2247 and o04 board 2266; 147 by t05 board 2608 and o04 board 2632. s16 batch A, item 1 of 5. ch01 on the batch (board 2867): build rc 0 with 0 warnings; rush-daemon 628/0, rush-lib 1252/0, rush-cli-client 398/0 on items 1-4; 45 of 48 mutants killed; in the e2e the daemon is gone 10.46 s after `daemon stop`, where s15's tree lingers 39.15 s. Gate: ch01 GATE OK board 2867 (tree 24fc1e6446) Commits folded into this step (2): - fe430fe236 rushd: a shutdown stuck on an await that ignores cancellation no longer hangs the daemon process (task 115) - 444107370d rushd: a daemon process that something else keeps running exits 2 s after the daemon stops (task 147) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A hot request reuses the latest input capture that started after it was received Swarm integration step 37; original commit 7ab68d689d (merge of r05 at a5e20e7c20). Scope: task 137. Brings r05's task 137: in a hot burst each request ran its own full-workspace project-config capture (about 0.09 s each), so n=32 ran 32 of them back to back and held the event loop for 3.1-3.3 s (board 2101). A request now reuses the latest capture that started after the request was received. CONFIRMED by t01 board 2303. s16 batch A, item 2 of 5 (ch01 board 2867; the suites and mutants are in item 1's message). Gate: ch01 GATE OK board 2867 (tree 3b55869396) Commits folded into this step (1): - a5e20e7c20 [rush-daemon] Reuse the latest input capture that started after a request was received Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-azure-storage-build-cache-plugin] The Azure build cache picks up a credential written after the daemon's first cloud read Swarm integration step 38; original commit 0f5e259316 (merge of swarm/r03-t101 at f4a425ada3). Scope: task 101. Brings r03's task 101: the Azure storage build cache provider kept its first container client for the daemon's lifetime, so a credential written later was never used (ch05 board 1202, ch02 board 1229). It now uses the credential cached after the first cloud request. CONFIRMED by ch05 board 2290. s16 batch A, item 3 of 5 (ch01 board 2867). Gate: ch01 GATE OK board 2867 (tree 38dd18ec39) Commits folded into this step (1): - f4a425ada3 [rush-azure-storage-build-cache-plugin] Use a credential that is cached after the first cloud request (task 101) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] build-cache.json accepts azureBlobStorageConfiguration.storageEndpoint Swarm integration step 39; original commit ac2e51d2d2 (merge of swarm/r03-t144 at 04c7af3fbb). Scope: task 144. Brings r03's task 144: rush-lib's build-cache.schema.json rejected azureBlobStorageConfiguration.storageEndpoint, which the Azure plugin has read since microsoft/rushstack PR 5664. CONFIRMED by ch05 board 2396. s16 batch A, item 4 of 5 (ch01 board 2867). Gate: ch01 GATE OK board 2867 (tree 0fbdb27c44) Commits folded into this step (1): - 04c7af3fbb [rush-lib] Allow storageEndpoint in build-cache.json's azureBlobStorageConfiguration (task 144) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A lingering daemon logs its PID and stop time, without a stack Swarm integration step 40; original commit 86fc1ff37e (merge of swarm/r01-t147-nit at 7a76edd6ef). Scope: task 147 NITs. Brings r01's fix for the NITs in t05 board 2608 and o04 board 2632 (board 2814): the report is a timestamped log line through onLog, not an error with frames. t05 read the delta (board 2832), and r01's run through the real entry point is board 2851. ch01 on the final tree: build rc 0 with 0 warnings, rush-daemon 629/0, 7 of 7 mutants killed. s16 batch A, item 5 of 5 (ch01 board 2867). Gate: ch01 GATE OK board 2867 (tree d3a1eb9dbc) Commits folded into this step (1): - 7a76edd6ef rushd: a lingering daemon logs its PID and stop time, without a stack (task 147 NITs) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] The client prints a cancelling line at once, agent output shows an operation's error after its own output, and a cancelled request reports never-started operations as not run Swarm integration step 41; original commit cba3c1f17b (merge of swarm/r04-t132-153-int at 4b495d5f81). Scope: tasks 132, 142 and 153. Brings r04's tasks 132 (after Ctrl-C, SIGTERM or SIGHUP during a daemon request the client prints one line at once, plus a notice when the daemon doesn't confirm), 142 (agent output prints a failed operation's error even when the operation wrote output first) and 153 (a cancelled request no longer reports never-started operations with an earlier request's result), re-tipped on d87702c813. Second agents: t05 board 2323 (132), t07 board 2623 (142), t05 board 2524 (153). ch01's e2e on ch01-sB: the cancelling line and the unconfirmed notice appear, and c is reported aborted, not SUCCESS. s16 batch B, item 1 of 5 (ch01 board 2984). Gate: ch01 GATE OK board 2984 (tree 6453dd938e) Commits folded into this step (3): - 85ed03e693 Say at once that a cancelled request waits for rushd to stop it (task 132) - 892c1592eb Print a failed operation's error even after its output in agent output (task 142) - 52b9d3fe3b Report never-started operations of a cancelled request as aborted (task 153) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Tests for a cancel while continuing work, and for the early failure offer Swarm integration step 42; original commit c4693204c1 (merge of swarm/r04-t108-nits-int at 97e473c08e). Scope: task 108(a) NITs. Brings r04's tests for the three gate NITs of task 108(a) (ch01 board 2678). Tests only; they pass on the old product, and ch01's mutants show they're live. s16 batch B, item 2 of 5 (ch01 board 2984). Gate: ch01 GATE OK board 2984 (tree b21b6b1f0b) Commits folded into this step (1): - 97e473c08e Test the three gate NITs of task 108 (a): a cancel while continuing work stops, and the early failure offer's silent and aborted operations (ch01 board 2678) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] The client relaunches the daemon after a startup helper exited, once its relaunch time passed Swarm integration step 43; original commit e073bd0f4b (merge of swarm/r03-t126 at da64013877). Scope: task 126. Brings r03's task 126: a client takes over a startup reservation whose helper is provably gone and whose daemon never became ready, instead of running in-process until `daemon stop --force` (t05 board 1574 and board 1652, row 13a). Second agent: t07 board 2751. ch01's e2e: the reservation is taken over after the relaunch time. s16 batch B, item 3 of 5 (ch01 board 2984). Gate: ch01 GATE OK board 2984 (tree a7707a430e) Commits folded into this step (1): - da64013877 rush-client-core: relaunch the daemon after a startup helper exited, once its relaunch time passed (task 126) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] After a daemon crash the client reaps its orphaned operations before it returns Swarm integration step 44; original commit 80219e0892 (merge of swarm/r06-t154-int2 at 25c7f0135c). Scope: task 154. Brings r06's task 154 (board 2382), re-tipped on c0e17d82ab (board 2747): after a daemon crash, the client reaps the orphaned operations before returning, and before `--no-daemon` runs. Second agent: t05 board 2689 on 14baba4338. ch01's e2e: the orphan is reaped after a crash. s16 batch B, item 4 of 5 (ch01 board 2984). Gate: ch01 GATE OK board 2984 (tree 2453314813) Commits folded into this step (2): - e79486fa37 [rush-client-core] Stop a crashed daemon's operations before reporting the crash (task 154) - 14baba4338 [rush-cli-client] Reclaim a crashed daemon before Rush runs in-process (task 154) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that an expired protected project takes no warm-set lease Swarm integration step 45; original commit 1b18fcc3eb (merge of swarm/r07-t76-nit at 6c6f638a2e). Scope: task 76 NIT. Brings r07's test for the task 76 NIT. Test only. s16 batch B, item 5 of 5 (ch01 board 2984). Gate: ch01 GATE OK board 2984 (tree 104ca5f27d) Commits folded into this step (1): - 6c6f638a2e [rush-daemon] Warm set: test that an expired protected project takes no lease Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] A hot request with no file change since the previous one skips the repo-state work Swarm integration step 46; original commit a9e930338f (merge of swarm/r07-t114-int at e883c78766). Scope: task 114. Brings r07's task 114 (daemon L3b, D20.3): RepoStateCache keeps the last repo-state capture, and a hot request whose files haven't changed since reuses it instead of running git status, ls-tree, the inputs snapshot and each operation's own-state hash again. It also moves assertCompatibleInputs and isGraphDefinitionPath into WorkspaceInputsComparison.ts with the same path list. Second agents: m01 board 2842 and t04 board 2897 on d0f571a420 (t114verify: 0 STALE rows, board 2829); addenda t04 board 3042 and m01 board 3031. ch01's e2e on ch01-sC: 30 of 30 steps equal to --no-daemon, 0 STALE. s16 batch C, item 1 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree 8299cd3e10) Commits folded into this step (4): - 5fbb206eb3 [package-deps-hash] Add RepoStateCache for processes that read the state of a repository repeatedly - b7904dd6d6 [rush-lib] Keep the state of the repository and reuse unchanged inputs snapshots in long-lived hosts - 7cb4c46d89 [rush-daemon] Skip comparing unchanged inputs snapshots on each request - d0f571a420 [package-deps-hash] Copy the index again when the sizes, attributes, configuration or filter change Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] An invalid build command line fails with native Rush's error and exit code instead of running in-process Swarm integration step 47; original commit f84ce3a3cb (merge of swarm/r04-t78-int at cf5921e40f). Scope: task 78. Brings r04's task 78: when a warm daemon's parser rejects a build command line, the client prints the daemon's parse error once and exits 2, where it used to fall back in-process and print about 29 lines. Second agent: o04 board 2801. ch01's e2e on ch01-sC: a warm `build --nope` with agent output returns rc 2, 2 lines, in 0.16 s, answered by the daemon. s16 batch C, item 2 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree d80d2a2ccb) Commits folded into this step (1): - cf5921e40f Fail an invalid build command line with native Rush's error and exit code instead of running it in-process (task 78) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Coalesced requests run with each requester's environment, and each gets its own telemetry key Swarm integration step 48; original commit b572eb50a0 (merge of swarm/r06-t90-int2 at 4b9a258212). Scope: tasks 81, 89 and 90. Brings r06's tasks 81 (coalesced requests no longer run once with the first requester's environment when an operation hashes an ignored variable they differ on), 89 (operations see process.env writes made by the same iteration's beforeExecuteIterationAsync, through a lazy per-participant environment) and 90 (a per-request telemetry key for operations in a coalesced batch), re-tipped on 1b51e7af08 (board 2747). Second agents: o04 board 783 and board 906, o05 board 1020, m03 board 1091 on adcb6f6e0f. It lands after r02's e7b848d6a7 (board 1166), which integration has. ch01's e2e on ch01-sC: two queued requests that differ in WT_SESSION run [ax, ay, c], as --no-daemon does. s16 batch C, item 3 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree 8a64bfc100) Commits folded into this step (6): - 5565ff57ce [rush-daemon] Don't coalesce requests that disagree on a hashed per-session variable - 56ef144e1b [rush-lib] Pass getOperationEnvironment to the before/after iteration hooks - e4d4e90711 [rush-daemon] Let requests share a run when a hashed variable is unset in one and empty in the other - aec3ac8433 [rush-daemon] Start each operation from process.env as it is when the operation starts - 36d9a3a16f [rush-lib] Add getOperationRequestId to the operation graph's iteration options - 3099d5945f [rush-daemon] Give each operation the request id of the request whose environment it gets Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] A build-cache hit no longer restores an earlier failing run's stderr Swarm integration step 49; original commit c3f9fa2ac7 (merge of swarm/o03-t157 at 5b2a400f29). Scope: task 157. Brings o03's task 157: a cache hit no longer writes an older failing run's stderr into rush-logs/.error.log, so a cache entry written after a failure isn't poisoned (ch02 board 2284, t05 board 2456). Second agent: t05 board 2704. ch01's e2e on ch01-sC: no error.log after either hit in the OK, ERR, OK sequence, and none in any cache entry. s16 batch C, item 4 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree 09d5eabe82) Commits folded into this step (1): - 5b2a400f29 Keep cache hits from restoring an earlier run's stderr (task 157) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] An iteration checks an operation's build-cache inputs only when it needs them Swarm integration step 50; original commit 633640c81c (merge of swarm/r07-t152 at 7e98034976). Scope: task 152. Brings r07's task 152: an executed iteration no longer computes the build-cache disabled reason for all of the all-project graph's operations (about 0.7 s at odsp-web) or splices finished records out of the queue one at a time (about 0.1 s). Second agent: m01 board 2727. ch01's e2e on ch01-sC: the cache arm's hits and misses equal --no-daemon. s16 batch C, item 5 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree 86cb10cdf4) Commits folded into this step (1): - 7e98034976 [rush-lib] Check the build cache inputs of an operation only when it's needed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] A public beta API lets a plugin runner run :incremental under task 106's guard Swarm integration step 51; original commit e39062a035 (merge of swarm/r06-t131-int2 at 52c86b3174). Scope: task 131. Brings r06's task 131: rush-lib exposes a @beta API so that a plugin runner such as rush-fstrace-plugin can run an `:incremental` phase under task 106's L1a guard, re-tipped on 1b51e7af08 (board 2747). Second agent: o03 board 2360 on cb0891d79d. The test-file conflict in 7b8e8c665b keeps both sides. s16 batch C, item 6 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree 07ac5f0139) Commits folded into this step (1): - cb0891d79d [rush-lib] Let operation runners from plugins use the incremental execution guard Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Answer a usage error only from a session that a build bound and checked Swarm integration step 52; original commit e12c8f3632 (merge of swarm/r04-t78-nit1 at 86a46e1d75). Scope: task 78 NIT 1. Brings r04's fix for o04's NIT 1 on task 78 (board 2801): the daemon answers a usage error only from a session that a build bound and checked. Second agent: o04 board 2948. s16 batch C, item 7 of 7 (ch01 board 3099). Gate: ch01 GATE OK board 3099 (tree 1af262606d) Commits folded into this step (1): - 86a46e1d75 Answer a usage error only from a session that a build bound and checked (task 78 follow-up, o04 board 2801 NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [node-core-library] Test the edges of LockFile's time zone check Swarm integration step 53; original commit b122243826 (merge of o07/lockfile-tzrule-nits at 48fbc7ee88). Scope: task 121 NITs. Brings o07's fixes for ch01's 4 test NITs on task 121 (board 2972): tests, one doc sentence and a type-none change file. The branch is in , fetched read-only for this merge. s17 batch D1, item 1 of 10 (ch01 board 3310). Gate: ch01 GATE OK board 3104 and board 3310 (tree 20d42b41b6) Commits folded into this step (1): - 48fbc7ee88 [node-core-library] Test the edges of LockFile's time zone check Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-protocol] Name the variables when a command's environment restarted the daemon Swarm integration step 54; original commit 898ff4dba1 (merge of swarm/r06-t88 at 04c6fb9d5a). Scope: task 88. Brings r06's fix for task 88 (board 2886): when a request restarts the daemon, or follows a restarted one, the client prints one line naming the differing variables (names only). Second agent: t07 board 2961. s17 batch D1, item 2 of 10. Gate: ch01 GATE OK board 3310 (tree ad9aa75f8c) Commits folded into this step (3): - ffbe43c152 [rush-daemon-protocol] Add the environmentChanged restart reason (task 88) - 8afd905246 [rush-daemon] Name the variables when the daemon restarts for an environment (task 88) - 04c6fb9d5a [rush-cli-client] Name the variables when a command's environment restarted the daemon (task 88) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A restart drain's waiter rechecks its tier Swarm integration step 55; original commit 075d2130b2 (merge of swarm/r05-t127-int at 236c184d94). Scope: task 127. Brings r05's task 127 follow-up (board 2436, re-tipped onto d87702c813 in board 2858): a Restart-tier change that is reverted under load no longer fails its waiter at 30 s. Second agent: o05 board 2552. s17 batch D1, item 3 of 10. Gate: ch01 GATE OK board 3310 (tree bdfad6af20) Commits folded into this step (2): - 961f007cae [rush-daemon] A request that waits for a restart drain rechecks whether it still needs the restart (task 127) - 0b1585667a [rush-daemon] Requests that wait for a restart drain share one recheck capture a second (task 127) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] ODSP_TELEMETRY_TAG no longer restarts the daemon Swarm integration step 56; original commit 442af4ed14 (merge of swarm/r02-t168-int at b9b7f9dedd). Scope: task 168. Brings r02's task 168 (board 2939, re-tipped onto batch B in board 3030): setting, changing or unsetting ODSP_TELEMETRY_TAG keeps the warm daemon. Second agents: t07 board 3004 and m03 board 2975. s17 batch D1, item 4 of 10. Gate: ch01 GATE OK board 3310 (tree eadadd0318) Commits folded into this step (3): - 4a2af75d2b [rush-lib] [rush-daemon] Keep the warm daemon when a request's telemetry tag changes (task 168) - efb14d246a [rush-daemon] Log an early failed result's telemetry once its continuing work settles (task 168) - c888d079b3 [rush-lib] Test that the telemetry flush wait clears its timer (task 168) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] The daemon's failure summary names the error, not the first buffered warning Swarm integration step 57; original commit edbe3f9065 (merge of swarm/r04-t170-int at 6219ed8e65). Scope: task 170. Brings r04's task 170 (board 2953) with its NIT fix 9630e0bd88 (board 3036), re-tipped onto batch B. Second agent: t01 board 2966 and board 3045. s17 batch D1, item 5 of 10. Gate: ch01 GATE OK board 3310 (tree cbdb7a5bb7) Commits folded into this step (3): - 781d08b278 Give the error first in a rejected request's message, not a warning written before it (task 170) - f1d8a34c90 Put the in-process fallback on the first line of the reason (task 170) - 9630e0bd88 Drop a colon that ends the fallback reason's first line (task 170, t01 NIT) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Check the installation again after a restart's waits Swarm integration step 58; original commit 13c4debd1f (merge of swarm/r03-t91g at b512743133). Scope: task 167. Brings r03's task 167 (board 2919): tests for ch01's surviving mutants on tasks 91 and 125, and rush-daemon checks the installation again after a restart's waits, before planning a successor. Second agent: t05 board 3064. s17 batch D1, item 6 of 10. Gate: ch01 GATE OK board 3310 (tree 6caa39900a) Commits folded into this step (1): - b512743133 rush-daemon: check the installation again after a restart's waits, before planning a successor (task 167) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] A parameter whose short name the tool also defines can be used by its long name Swarm integration step 59; original commit 49695c5d00 (merge of swarm/r03-t172 at 4526357153). Scope: task 172. Brings r03's task 172 (board 3008): rush update-cloud-credentials --delete no longer fails with an ambiguous -d. Second agent: o03 board 3054. s17 batch D1, item 7 of 10. Gate: ch01 GATE OK board 3310 (tree 29b72dd480) Commits folded into this step (1): - 4526357153 [ts-command-line] A parameter whose short name the tool also defines can be used by its long name (task 172) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-azure-storage-build-cache-plugin] Cache Azure credentials per storage endpoint Swarm integration step 60; original commit d950fba8d3 (merge of swarm/r03-t144b-x101 at 5e91eed724). Scope: task 164. Brings r03's task 164 (board 2765), merged with task 101: the Azure build cache keys a saved credential by its storage endpoint, and the schema says that http endpoints need a SAS. Second agent: ch05 board 2940. s17 batch D1, item 8 of 10. Gate: ch01 GATE OK board 3310 (tree b0ee664f20) Commits folded into this step (1): - 2a744907c2 [rush-azure-storage-build-cache-plugin] Save credentials per storageEndpoint, and require an http or https storageEndpoint (task 164) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test the cancellation notice when the client never asked rushd to stop Swarm integration step 61; original commit 4d06054101 (merge of swarm/r04-t132-nit1 at 8f0016995a). Scope: task 132 NIT 1. Brings r04's fix for ch01's NIT 1 on task 132 (board 3094). Second agent: t05 board 3172. s17 batch D1, item 9 of 10. Gate: ch01 GATE OK board 3310 (tree 7e80f904a2) Commits folded into this step (1): - 8f0016995a Test the cancellation notice when the client never asked rushd to stop the request (task 132 NIT 1, ch01 board 2984) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Test that a takeover keeps a reservation that replaced the abandoned one Swarm integration step 62; original commit d06e2ded5e (merge of swarm/r03-t126-nit2 at 40ca8cd335). Scope: task 126 NIT 2. Brings r03's fix for ch01's NIT 2 on task 126 (board 2984, board 3089). ch01 checked it with mutant K126 in the gate. s17 batch D1, item 10 of 10. Gate: ch01 GATE OK board 3310 (tree 8061bc8677) Commits folded into this step (1): - 40ca8cd335 [rush-client-core] Test that a takeover keeps a reservation that replaced the abandoned one (task 126, ch01 NIT 2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Type PhasedCommandEngineUsageError's options without ErrorOptions Swarm integration step 63; original commit f6d03bcffd (merge of swarm/r04-t78-erroropts at 17c6dacf70). Scope: task 196. Brings r04's fix for ch01's BUG board 3247 (board 3285): the constructor takes `options?: { cause?: unknown }` instead of `ErrorOptions`, so consumers of the rush-lib typings compiled with an older lib, such as the three es2017 build-tests projects, build again. Second agent: o04 board 3354. s17 batch D2, item 1 of 6. Gate: ch01 GATE OK board 3514 (tree 813312a4d4) Commits folded into this step (1): - 17c6dacf70 Type PhasedCommandEngineUsageError's options without ErrorOptions, so that consumers of the rush-lib typings with an older lib compile (task 78 fix, ch01 board 3247) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] A request that waits for a daemon restart says why Swarm integration step 64; original commit 12732cc337 (merge of swarm/r05-t166-int3 at 218ab4f8c3). Scope: task 166. Brings r05's task 166 (board 3041, board 3112), re-tipped with 170-int for batch D2 (board 3271): a request that waits for a daemon restart says why and what it waits for, within 0.5 s and every 25 s after that, and a waiting rushx-client says so with its own prefix. Errors name the real cause instead of "its environment". Second agent: o05 board 3168. s17 batch D2, item 2 of 6. Gate: ch01 GATE OK board 3514 (tree 17650f1da7) Commits folded into this step (6): - 2a0b2adb63 rush-daemon-protocol: a workspaceInputsChanged restart reason, and restart-wait details on queue positions (task 166) - a0c391d2ac rush-lib: WorkspaceRuntimeFingerprintCache.changedInstallationPaths (task 166) - 282d202be7 rush-client-core: name daemon restart causes and report restart waits and input admission (task 166) - b90a73b279 rush-daemon: say why a request waits for a daemon restart, in its queue positions and admission errors (task 166) - 20f173e57e rush-cli-client: say why and for what a request waits for a daemon restart, on pipes too, and repeat it every 25 s (task 166) - 2e3c61e239 rush-cli-client: no restart wait line once a cancellation is requested (task 166 with 132) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] A warm daemon request parses its command line once Swarm integration step 65; original commit a15fb1c70a (merge of swarm/r05-t146-int3 at d46b1047e7). Scope: task 146. Brings r05's task 146 (board 2739, re-tipped in board 3163 and board 3403): a warm request parses its command line once instead of twice, and re-derives a JSON configuration file only when the file's text changed. 146-int3 adds 170-int and the one-line test fix that ch01's BLOCK board 3328 asked for. Second agent: t01 board 2912. s17 batch D2, item 3 of 6. Gate: ch01 GATE OK board 3514 (tree 0e2dfdba29) Commits folded into this step (3): - 475db85a16 [rush-lib] An engine host parses each JSON configuration file again only if its text changed (task 146) - 4937fb600d [rush-daemon] Parse the command line of a warm request once, not twice (task 146) - d46b1047e7 rush-daemon: expect the error before the reused parse's diagnostics, as task 170 orders them (task 146, ch01 board 3328) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Keep the untracked cache and fsmonitor token when RepoStateCache copies the index again Swarm integration step 66; original commit 4c28b44283 (merge of swarm/r07-t169 at effeec5b44). Scope: task 169. Brings r07's task 169 (board 3170): when the copied index records the same paths, the new copy keeps the old copy's untracked cache and fsmonitor token. The daemon's first git status after git add, restore --staged, reset --hard or checkout -- drops from 0.94-1.44 s to 0.28-0.45 s. Second agents: t04 board 3334 and m01 board 3440. s17 batch D2, item 4 of 6. Gate: ch01 GATE OK board 3514 (tree d362c9d055) Commits folded into this step (1): - effeec5b44 Keep the untracked cache and file system monitor state when RepoStateCache copies the index again Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a usage error goes to in-process Rush while a reload is pending Swarm integration step 67; original commit 3ff87ea4b8 (merge of swarm/r04-t78-nit7 at aae9f29de0). Scope: task 78 NIT 7. Brings r04's fix for ch01's NIT 7 on task 78 (board 3099, board 3160): a new test pins the `!#forceReload` conjunct. No behavior change. Second agent: o04 board 3188. s17 batch D2, item 5 of 6. Gate: ch01 GATE OK board 3514 (tree 2fcaab2166) Commits folded into this step (1): - aae9f29de0 Test that a usage error goes to in-process Rush while a reload is pending (task 78 follow-up, ch01 board 3099 NIT 7) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Test more RepoStateCache changes Swarm integration step 68; original commit ddf37c86ed (merge of swarm/r07-t114-nits at c4b24fad18). Scope: task 114 NITs 1-4. Brings r07's test-only fix for ch01's NITs 1-4 on task 114 (board 3099, board 3169): more RepoStateCache tests in package-deps-hash. No source changes. ch01 checked them with mutants P114N (4 of 4 killed) in the gate. s17 batch D2, item 6 of 6. Gate: ch01 GATE OK board 3514 (tree 0466deb3dd) Commits folded into this step (1): - c4b24fad18 [package-deps-hash] Test more RepoStateCache changes that only the file list, the change time or a new configuration reveal Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] The daemon can keep warm heft run-watch workers for incremental builds, off by default Swarm integration step 69; original commit 053872af71 (merge of o07/l1b-on-c at 7088556062). Scope: task 107. Brings o07's task 107, L1b stage 1 (board 2762; landing candidate board 3259): with `daemon.warmWorkers` (or RUSH_DAEMON_WARM_WORKERS=1) and `daemon.incrementalBuilds`, the daemon keeps a `heft run-watch` worker alive for each operation whose rush-project.json settings set `allowDaemonWarmWorker`, and sends it the next incremental run, as rush start does. It is off by default, and swarm-dogfood sets none of these. 7088556062 is dd57043877 (board 3166) plus two commits for the verdicts' NITs. Second agent: t03 CONFIRMED board 2898. s17 batch D3, item 1 of 8. Gate: ch01 GATE OK board 3747 (tree 7bdb42fe64) Commits folded into this step (16): - cc3ad016da [heft-lint-plugin] Add a lintInWatchMode option - 6e403a9135 [rush-lib] Let incremental guard callers accept bundled outputs and skip build cache reads - 45565a0c3e [rush-lib] Keep warm workers alive between Rush daemon builds - 1a651f544b [rush-lib] Require an opt-in for Rush daemon warm workers - cfd3107c10 [rush-lib] Run the initial command after a failed run on a reused warm worker - 7cca36dd3a [heft-typescript-plugin] Never throw from the realpath function for missing paths - 2f8fd2cca0 [rush-lib] Report why a warm worker closed if the build cache restores its operation - 565e84c5bb [rush-lib] Say when a warm worker was closed between builds - e6bc0d81a5 [rush-lib] Drop an unreachable exception from the incremental guard's initial-only marks - 7a89294333 [rush-lib] Start warm workers with a TypeScript file watcher that sees replaced files - 26c35d188d [rush-lib] Compare content-hashed bundles when checking a warm worker's outputs - 31182e0002 [rush-lib] Stop using a warm worker after it renamed a content-hashed output - 89d541e9ab [rush-lib] Run the initial command after a warm worker's input folder was recreated - c2b58c25ad Document two more costs of Rush daemon warm workers - 085e3a6e69 Name only the top recreated input folders in the guard's reason - 7088556062 Document warm runs that succeed after a missing file, and what Heft's watchers miss Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] The daemon logs each shutdown with its reason, and exits when its socket file is lost Swarm integration step 70; original commit 689084765b (merge of swarm/o04-t151-x156 at 5503c45528). Scope: tasks 151 and 156. Brings o04's task 151 (board 3120) and r01's task 156 (board 3253), merged together by o04 (board 3190) because they conflict in one doc comment. 151: rushd logs each shutdown with its reason, time and PID, and its ready line gets the time and PID. 156: a daemon whose socket file was deleted or replaced notices within 5 s and exits once no request runs (initiator socketLost), so the next build starts a new daemon without the 15 s wait and the in-process fallback. Second agents: t02 CONFIRMED 151 board 3249 and 156 board 3302. s17 batch D3, item 2 of 8. Gate: ch01 GATE OK board 3747 (tree adbb8b5631) Commits folded into this step (3): - b5e7243165 rush-daemon: a daemon whose socket file was deleted or replaced exits once idle (task 156) - ad32ffa563 [rush-daemon] Log each shutdown with its reason and PID, and add the time and PID to the ready line - 5503c45528 Name a lost socket in the daemon's shutdown line Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Daemon stop during a startup, and daemon status with no daemon Swarm integration step 71; original commit a9ba6807cf (merge of swarm/o04-t150-x133 at 2821ea5c98). Scope: tasks 133 and 150. Brings o04's tasks 133 and 150, re-tipped on s16 batch B (board 3127). 133: a plain `daemon stop` during a startup either stops the daemon (rc 0, shutdownAccepted) or exits rc 1 with a line saying it didn't, instead of notRunning rc 0 while the daemon comes up anyway. 150: `rush-client daemon status` says that no daemon is running instead of "Could not connect to daemon at ". Second agent: t05 CONFIRMED 133 board 2489 and 150 board 2651. s17 batch D3, item 3 of 8. Gate: ch01 GATE OK board 3747 (tree d37576d243) Commits folded into this step (4): - f60c3e5721 [rush-client-core] Add connectToStartingDaemonAsync() for callers that must not leave a daemon running - eefafdf4e2 [rush-cli-client] Make daemon stop wait for a daemon that is still starting, and stop it - 421c66682b [rush-client-core] Fail at once when a started daemon exits before its starting client connects - 2ff5bfdd53 [rush-cli-client] Say that no daemon is running when daemon status finds none Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] afterExecuteRequestAsync runs once for each request, with that request's results Swarm integration step 72; original commit 2f4ca9f5dd (merge of swarm/r02-t65-nits at 1f1c778570). Scope: task 65. Brings r02's task 65, rushstack half (board 3046; NITs board 3312): the new `afterExecuteRequestAsync` hook runs once for each request, with only that request's results, command name, environment and terminal, including a warm request that needs no iteration and each of several coalesced requests. It also fixes an early-result bug that reported waiting operations as SKIPPED. Second agent: o03 CONFIRMED board 3226 and board 3371. s17 batch D3, item 4 of 8. Gate: ch01 GATE OK board 3747 (tree 8775aa859f) Commits folded into this step (3): - 0e019d1662 [rush-lib] [rush-daemon] Add afterExecuteRequestAsync, invoked once for each request (task 65) - 72242e4bbc [rush-daemon] [rush-lib] An early result leaves out operations that have not finished (task 65) - 1f1c778570 Test the native afterExecuteRequestAsync status and command name, and document which requests get no call Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Report the output behind warnings given after an operation succeeded Swarm integration step 73; original commit c34fa4f992 (merge of swarm/r04-t165 at 15351b01af). Scope: task 165. Brings r04's task 165 (board 3076): when an operation succeeds and then gets warnings because its build cache entry can't be written, agent output prints the lines that explain the warnings instead of `(no output)`. Second agent: t07 CONFIRMED board 3206. s17 batch D3, item 5 of 8. Gate: ch01 GATE OK board 3747 (tree eaae7633ad) Commits folded into this step (1): - 15351b01af Report the output behind warnings given after an operation succeeded (task 165) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep the warm daemon across Claude Code's per-command variables Swarm integration step 74; original commit 20a1b34711 (merge of swarm/r02-t188 at da4de811a7). Scope: task 188. Brings r02's task 188 (board 3243): the daemon's identity ignores the variables Claude Code's Bash tool sets for each command, so a warm daemon keeps its pid when Claude Code runs a command. Second agent: t07 CONFIRMED board 3296. s17 batch D3, item 6 of 8. Gate: ch01 GATE OK board 3747 (tree 02cb5d8683) Commits folded into this step (1): - da4de811a7 [rush-lib] Keep the warm daemon across Claude Code's per-command variables (task 188) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Say when a failed operation ran its incremental command Swarm integration step 75; original commit ce7a5beb3c (merge of swarm/r06-t149-int2 at 9b0f67f886). Scope: task 149. Brings r06's task 149 (board 2398), re-tipped on s16 (board 3237): agent output says when a failed operation ran its incremental command. Second agent: o05 CONFIRMED at odsp-web scale, board 2495. s17 batch D3, item 7 of 8. Gate: ch01 GATE OK board 3747 (tree 2e894960af) Commits folded into this step (1): - f59b64132a [rush-daemon] Say in agent output when a failed operation ran its incremental command (task 149) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Detect in-place output edits and nested output deletes Swarm integration step 76; original commit 0eda8967aa (merge of swarm/r02-t82-int at d0eef2ea21). Scope: task 82. Brings r02's task 82 (board 884; re-tip board 2830): in-place output edits and nested output deletes are detected, where they gave a wrong "no op" and a consumer built on top poisoned its own cache entry. Second agents: t03 board 952, t04 board 955 and o04 board 956. It raises idle RSS until task 119 lands; coord accepted that cost for s17 in D34 (board 3423), and it merges last in D3. s17 batch D3, item 8 of 8. Gate: ch01 GATE OK board 3747 (tree 1b18bd3257) Commits folded into this step (3): - 9ff625ceeb [rush-daemon] Detect in-place and nested output edits of selected warm operations (task 82) - 3b919d17ca [rush-daemon] Walk warm operations' outputs on a worker pool (task 82 option b) - 04dfbe72a5 [rush-daemon] Limit the young generation of output digest workers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Say why a command runs in-process, keep the user's flags there, and send `rush -q ` to the daemon Swarm integration step 77; original commit 59f096cfc5 (merge of swarm/r04-t60 at 6949891851). Refs: tasks 60 and 36. Scope: 60 + 36. Brings 60 + 36 (branch swarm/r04-t60). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 1 of 18. Gate: ch01 GATE OK board 4131 (tree 330bc72f83) Commits folded into this step (1): - 6949891851 Say why a command runs in-process, keep the user's flags there, and send `rush -q ` to the daemon (tasks 60 and 36) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Task 97: stop printing "An unknown error occurred." for ended rushx scripts Swarm integration step 78; original commit d34d57a0cf (merge of swarm/r04-t97 at cee4b61dce). Scope: task 97. Brings 97 (branch swarm/r04-t97). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 2 of 18. Gate: ch01 GATE OK board 4131 (tree f7530b697a) Commits folded into this step (1): - cee4b61dce Task 97: stop printing "An unknown error occurred." for ended rushx scripts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] After a client leaves a shared iteration, unstarted work that only departed clients needed is skipped Swarm integration step 79; original commit 2890025999 (merge of swarm/r05-t99-int at 1cac4735d0). Refs: task 99. Scope: task 99-int. Brings 99-int (branch swarm/r05-t99-int). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 3 of 18. Gate: ch01 GATE OK board 4131 (tree 61834a6fd6) Commits folded into this step (1): - c60d5fc80e [rush-daemon] After a client leaves a shared iteration, unstarted work that only departed clients needed is skipped (task 99) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test legacy skip records for rebuild and disabled cache Swarm integration step 80; original commit 41e0b83549 (merge of swarm/r03-t180-nit1 at 60aec3136f). Refs: n180 NIT. Scope: task 180-nit1. Brings 180-nit1 (branch swarm/r03-t180-nit1). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 4 of 18. Gate: ch01 GATE OK board 4131 (tree faf1d81f09) Commits folded into this step (3): - 59abe93a06 rush-lib: keep legacy skip detection from trusting replaced outputs - 430fd46a7f rush-lib: test which strategies delete the legacy skip records (180 NIT 1) - 60aec3136f rush-lib: test legacy skip records for rebuild and disabled cache (n180 NIT) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Reuse the state of unchanged projects when taking a new inputs snapshot Swarm integration step 81; original commit 0d7d25cff3 (merge of swarm/r07-t193 at 745e7052bb). Scope: task 193. Brings 193 (branch swarm/r07-t193). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 5 of 18. Gate: ch01 GATE OK board 4131 (tree 45f742b87d) Commits folded into this step (1): - 745e7052bb [rush-lib] Reuse the state of unchanged projects when taking a new inputs snapshot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Say which batches write every result before logging Swarm integration step 82; original commit db2c2c3771 (merge of swarm/m03-t186-nit1 at 9cf1d8ac48). Refs: task 186 NIT 1. Scope: task 186-nit1. Brings 186-nit1 (branch swarm/m03-t186-nit1). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 6 of 18. Gate: ch01 GATE OK board 4131 (tree 2cc84122df) Commits folded into this step (2): - 642889338f [rush-daemon] Write a request's result before logging its telemetry (task 186) - 9cf1d8ac48 [rush-daemon] Say which batches write every result before logging (task 186 NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Keep the socket's error when the lockfile's also fails Swarm integration step 83; original commit 8be98a7c20 (merge of swarm/r01-t197-nit1 at db5147164d). Refs: task 197 NIT N1. Scope: task 197-nit1. Brings 197-nit1 (branch swarm/r01-t197-nit1). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 7 of 18. Gate: ch01 GATE OK board 4131 (tree ee18e8e5aa) Commits folded into this step (4): - 81dd79c948 rush-daemon-transport: ENOTDIR reads as nothing there; removeOwnFile always releases (task 197) - 4e4bdeecbd rush-daemon-transport: a listener closes and releases everything when a deletion fails (task 197) - 1ed80ba18a rush-daemon: test that a stopped daemon exits at once when it cannot delete its files (task 197) - db5147164d rush-daemon-transport: keep the socket's error when the lockfile's also fails (task 197 NIT N1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Settle a backpressured frame send when its socket ends or closes Swarm integration step 84; original commit 57e6375fa0 (merge of swarm/m03-t209 at f2193d5048). Scope: task 209. Brings 209 (branch swarm/m03-t209). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 8 of 18. Gate: ch01 GATE OK board 4131 (tree 83d60b4fb4) Commits folded into this step (1): - f2193d5048 Settle a backpressured frame send when its socket ends or closes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Test how rush-client-core waits for a daemon that another process starts Swarm integration step 85; original commit 11d7df2b7f (merge of o07/t95-tests at e8e0b22a14). Scope: 211 tests. Brings 211 tests (branch o07/t95-tests). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 9 of 18. Gate: ch01 GATE OK board 4131 (tree 243d4de348) Commits folded into this step (1): - e8e0b22a14 Test how rush-client-core waits for a daemon that another process starts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Say which process rush-client waits for in its agent-output line Swarm integration step 86; original commit 48c6443d7c (merge of o07/t95-n1 at 4078fa8e9e). Scope: 211 n1. Brings 211 n1 (branch o07/t95-n1). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 10 of 18. Gate: ch01 GATE OK board 4131 (tree ddcd9fb66b) Commits folded into this step (1): - 4078fa8e9e Say which process rush-client waits for in its agent-output line Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Escape control characters in restart variable names (task 184) (and 2 more) Swarm integration step 87; original commit 723afd2d3a (merge of swarm/r06-t184-int2 at a205528877). Scope: task 184-int2. Brings 184-int2 (branch swarm/r06-t184-int2). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 11 of 18. Gate: ch01 GATE OK board 4131 (tree e017e4e262) Commits folded into this step (3): - c8d4efbb74 rush-daemon: escape control characters in restart variable names (task 184) - d8243a1ec0 rush-client-core: add DaemonRestartFailedError with the restart reason (task 184) - 080c28b844 rush-cli-client: say why the daemon restarted when its successor fails (task 184) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test the default shutdown deadline of a daemon process Swarm integration step 88; original commit f0e3dd5082 (merge of swarm/r01-t115-nit1 at c4c4753313). Refs: 115 NIT 2. Scope: task 115-nit1. Brings 115-nit1 (branch swarm/r01-t115-nit1). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 12 of 18. Gate: ch01 GATE OK board 4131 (tree 7edd3fca13) Commits folded into this step (2): - 419bc5cdc7 rush-daemon: test that releaseForExit keeps everything while a recorded child runs (115 NIT 1) - c4c4753313 rush-daemon: test the default shutdown deadline of a daemon process (115 NIT 2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a request that needs a restart for its environment does not check its inputs again during the drain Swarm integration step 89; original commit 627f229690 (merge of swarm/r05-t127-nit1 at 67f9972263). Refs: task 127, ch01 NIT 1. Scope: task 127-nit1. Brings 127-nit1 (branch swarm/r05-t127-nit1). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 13 of 18. Gate: ch01 GATE OK board 4131 (tree 66951bb8d6) Commits folded into this step (1): - 67f9972263 [rush-daemon] Test that a request that needs a restart for its environment does not check its inputs again during the drain (task 127, ch01 NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-azure-storage-build-cache-plugin] Test the storageEndpoint pattern in the plugin's options schema Swarm integration step 90; original commit 66d76593af (merge of swarm/r03-t164-nit4 at 0bfdc117bd). Refs: task 164 NIT 4. Scope: task 164-nit4. Brings 164-nit4 (branch swarm/r03-t164-nit4). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 14 of 18. Gate: ch01 GATE OK board 4131 (tree 1efb607588) Commits folded into this step (1): - 0bfdc117bd [rush-azure-storage-build-cache-plugin] Test the storageEndpoint pattern in the plugin's options schema (task 164 NIT 4) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon][rush-cli-client] Test the restart reason's file cap, a mutation's reason and the result line's client name Swarm integration step 91; original commit 928e957038 (merge of swarm/r05-t166-nits at 702ff67bfe). Scope: task 166-nits. Brings 166-nits (branch swarm/r05-t166-nits). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 15 of 18. Gate: ch01 GATE OK board 4131 (tree 49e02edbfb) Commits folded into this step (1): - 702ff67bfe [rush-daemon][rush-cli-client] Test the restart reason's file cap, a mutation's reason and the result line's client name Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Test that a new copy of the Git index keeps no caches when only its entry count or version differs Swarm integration step 92; original commit 8d237df633 (merge of swarm/r07-t169-nits at bc8796720c). Scope: task 169-nits. Brings 169-nits (branch swarm/r07-t169-nits). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 16 of 18. Gate: ch01 GATE OK board 4131 (tree a0397e74b5) Commits folded into this step (1): - bc8796720c [package-deps-hash] Test that a new copy of the Git index keeps no caches when only its entry count or version differs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test the fallback line for a multi-line reason from a request that rushd sends back Swarm integration step 93; original commit 77ad5cecb1 (merge of swarm/r04-t170-nit3 at da22c11bcf). Refs: task 170 NIT 3, ch01 board 3310. Scope: task 170-nit3. Brings 170-nit3 (branch swarm/r04-t170-nit3). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 17 of 18. Gate: ch01 GATE OK board 4131 (tree 257aefda0e) Commits folded into this step (1): - da22c11bcf Test the fallback line for a multi-line reason from a request that rushd sends back (task 170 NIT 3, ch01 board 3310) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-typescript-plugin] Use the system's own watchFile when TypeScript has no WatchFileKind Swarm integration step 94; original commit 033744a8f4 (merge of o07/heft-ts-watchfile-oldts at 622a1337ec). Scope: 171 + NIT 1 + old-TS fix. Brings 171 + NIT 1 + old-TS fix (branch o07/heft-ts-watchfile-oldts). Second agents and evidence: ch01's GATE OK board 4131. s18 batch E1, item 18 of 18. Gate: ch01 GATE OK board 4131 (tree 1bfce0c609) Commits folded into this step (3): - 8aa4f3bd96 Notice when a missing root file comes back in heft-typescript-plugin's watch mode - c42dc6a9de Create heft-typescript-plugin's TypeScript system with a tested function - 622a1337ec Use the system's own watchFile when TypeScript has no WatchFileKind Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Report a reclaim's stopped operations to a callback (task 177) (and 5 more) Swarm integration step 95; original commit cefd76b256 (merge of swarm/r06-t177-int3 at a08d04af45). Scope: task 177-int3. Brings 177-int3 (branch swarm/r06-t177-int3). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 1 of 21. Gate: ch01 GATE OK board 4527 (tree 9346b7a8e0) Commits folded into this step (6): - ec0b9d69d0 [rush-daemon-transport] Report a reclaim's stopped operations to a callback (task 177) - b695ac0fe6 [rush-client-core] Pass a reclaim's stopped operations to onOrphansReaped (task 177) - b4a398f810 [rush-cli-client] Say what a reclaim stopped in one rush-client line (task 177) - 788d0d18a8 [rush-cli-client] Test the reclaim line when a start reclaims an exited daemon (task 177) - 353a162709 [rush-daemon-transport] Test reclaim reports of killed and reclaimed operation groups (task 177) - d9f98ea659 [rush-cli-client] Test the rushx-client reclaim line (task 177) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Name a daemon that exited without shutting down after a client reclaimed it Swarm integration step 96; original commit 5f7356e706 (merge of swarm/o04-t192-int2 at ee5eab70f6). Refs: task 192. Scope: task 192-int2. Brings 192-int2 (branch swarm/o04-t192-int2). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 2 of 21. Gate: ch01 GATE OK board 4527 (tree b4b55cdde0) Commits folded into this step (1): - dd647302bb Name a daemon that exited without shutting down after a client reclaimed it (task 192) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Start a client's launcher-log line on a line of its own when the log ends inside a line (task 213) (and 1 more) Swarm integration step 97; original commit 51038605a3 (merge of swarm/o04-t213-int3 at 0a683e346b). Scope: task 213-int3. Brings 213-int3 (branch swarm/o04-t213-int3). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 3 of 21. Gate: ch01 GATE OK board 4527 (tree 5090a6cce4) Commits folded into this step (2): - 6923a47821 Start a client's launcher-log line on a line of its own when the log ends inside a line (task 213) - 1f28075503 Stat the launcher log without leaving its type in Node's shared stat array (task 213, fix 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep rushd running, and stopping cleanly, when nothing reads its output Swarm integration step 98; original commit 96641539ef (merge of swarm/o04-t195 at e2da590f35). Refs: task 195. Scope: task 195. Brings 195 (branch swarm/o04-t195). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 4 of 21. Gate: ch01 GATE OK board 4527 (tree 82ea3c31c4) Commits folded into this step (1): - e2da590f35 Keep rushd running, and stopping cleanly, when nothing reads its output (task 195) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that an early result's iteration measure ends at the result Swarm integration step 99; original commit 141b0a16c8 (merge of swarm/r02-t168-nit1 at 233821c47f). Refs: 168 NIT, board 3617. Scope: task 168-nit1. Brings 168-nit1 (branch swarm/r02-t168-nit1). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 5 of 21. Gate: ch01 GATE OK board 4527 (tree eba7b8942a) Commits folded into this step (1): - 233821c47f [rush-daemon] Test that an early result's iteration measure ends at the result (168 NIT, board 3617) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Allow "disableBuildCache" on phased commands in command-line.json Swarm integration step 100; original commit 91abbea65b (merge of swarm/r03-t205 at e0bdf1911c). Scope: task 205. Brings 205 (branch swarm/r03-t205). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 6 of 21. Gate: ch01 GATE OK board 4527 (tree 80b98db7aa) Commits folded into this step (1): - e0bdf1911c rush-lib: allow "disableBuildCache" on phased commands in command-line.json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Drain stdout and stderr before exiting on failure Swarm integration step 101; original commit 9e56400fb5. Refs: task 204. Brings 204 (no branch in the shared repo, merged by hash). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 7 of 21. Gate: ch01 GATE OK board 4527 (tree 8c715ff8c7) Commits folded into this step (1): - 7d9ea1e0d6 [rush-lib] Drain stdout and stderr before exiting on failure (task 204) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Compare the content of Git configuration files in RepoStateCache Swarm integration step 102; original commit 896e68fa9c (merge of swarm/r07-t194 at 20f59f1515). Scope: task 194. Brings 194 (branch swarm/r07-t194). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 8 of 21. Gate: ch01 GATE OK board 4527 (tree b2e35929ff) Commits folded into this step (1): - 20f59f1515 [package-deps-hash] Compare the content of Git configuration files in RepoStateCache Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Take the inputs snapshot while validating the graph inputs Swarm integration step 103; original commit 337fc5fb97 (merge of swarm/r07-t212 at 1dccd5c6f1). Scope: task 212. Brings 212 (branch swarm/r07-t212). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 9 of 21. Gate: ch01 GATE OK board 4527 (tree 8552dfca4d) Commits folded into this step (3): - 9a551270e8 [package-deps-hash] Start "git status" before examining the additional files - a1ddac7181 [rush-lib] Read the repository state while checking a long-lived engine's graph inputs - 1dccd5c6f1 [rush-daemon] Take the inputs snapshot while validating the graph inputs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that a warm worker survives an iteration that skips its operation Swarm integration step 104; original commit dfa693cccd. Brings 107-nit4 (no branch in the shared repo, merged by hash). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 10 of 21. Gate: ch01 GATE OK board 4527 (tree 4ec8857841) Commits folded into this step (1): - 26a0653cdd Test that a warm worker survives an iteration that skips its operation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft] Never exit with code 0 after an error Swarm integration step 105; original commit c9325021e7 (merge of swarm/r03-t200 at 18e59c6665). Scope: task 200. Brings 200 (branch swarm/r03-t200). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 11 of 21. Gate: ch01 GATE OK board 4527 (tree cf09155465) Commits folded into this step (1): - 18e59c6665 [heft] Never exit with code 0 after an error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Fix rush-lib critical path lengths that depended on visit order Swarm integration step 106; original commit 36d08b63df (merge of swarm/m03-t215 at af3b1a5aa5). Scope: task 215. Brings 215 (branch swarm/m03-t215). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 12 of 21. Gate: ch01 GATE OK board 4527 (tree 425bdd301b) Commits folded into this step (1): - af3b1a5aa5 Fix rush-lib critical path lengths that depended on visit order Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Don't reinstall the package manager because of a stale lock alone Swarm integration step 107; original commit 7014dc08e0 (merge of swarm/r03-t201 at e2aadba3a7). Scope: task 201. Brings 201 (branch swarm/r03-t201). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 13 of 21. Gate: ch01 GATE OK board 4527 (tree 83b98b7486) Commits folded into this step (1): - e2aadba3a7 [rush-lib] Don't reinstall the package manager because of a stale lock alone Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Test a send on a connection that already closed Swarm integration step 108; original commit 75a68e09d2 (merge of swarm/m03-t209-nit1 at 15f80059fe). Refs: task 209 NIT 1. Scope: task 209-nit1. Brings 209-nit1 (branch swarm/m03-t209-nit1). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 14 of 21. Gate: ch01 GATE OK board 4527 (tree cb6fb308c1) Commits folded into this step (1): - 15f80059fe [rush-daemon-transport] Test a send on a connection that already closed (task 209 NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Reuse unchanged operation state hashes across iterations Swarm integration step 109; original commit a94072e8ee (merge of swarm/r07-t221 at f6ea91f25b). Scope: task 221. Brings 221 (branch swarm/r07-t221). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 15 of 21. Gate: ch01 GATE OK board 4527 (tree 547cffd351) Commits folded into this step (1): - f6ea91f25b [rush-lib] Reuse unchanged operation state hashes across iterations Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Skip the restart notice when a wait line already gave its cause Swarm integration step 110; original commit d68a31ffce (merge of swarm/r06-t222 at 672eef89f6). Refs: task 222. Scope: task 222. Brings 222 (branch swarm/r06-t222). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 16 of 21. Gate: ch01 GATE OK board 4527 (tree 5aca3fba63) Commits folded into this step (1): - 672eef89f6 [rush-cli-client] Skip the restart notice when a wait line already gave its cause (task 222) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test that the early daemon check reads "enabled" only inside the daemon block Swarm integration step 111; original commit fb6df36f6e (merge of swarm/r04-t60-nit2 at 91b9f8abf6). Refs: task 60 NIT 2, ch01 board 4131. Scope: task 60-nit2. Brings 60-nit2 (branch swarm/r04-t60-nit2). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 17 of 21. Gate: ch01 GATE OK board 4527 (tree b7717d801e) Commits folded into this step (1): - 91b9f8abf6 Test that the early daemon check reads "enabled" only inside the daemon block (task 60 NIT 2, ch01 board 4131) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core, rush-cli-client] Test the startup cases that n133's mutants left untested (ch01 NITs 5-8) Swarm integration step 112; original commit 2fabfdd173 (merge of swarm/o04-t133-nits-int at d90ccebd9d). Scope: task 133-nits. Brings 133-nits (branch swarm/o04-t133-nits-int). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 18 of 21. Gate: ch01 GATE OK board 4527 (tree 2223e4225f) Commits folded into this step (1): - bfddf14b39 [rush-client-core, rush-cli-client] Test the startup cases that n133's mutants left untested (ch01 NITs 5-8) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Stat sockets with { bigint: true } Swarm integration step 113; original commit 4c623537e7 (merge of swarm/r01-t210 at 1f23d6b61e). Refs: task 210. Scope: task 210. Brings 210 (branch swarm/r01-t210). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 19 of 21. Gate: ch01 GATE OK board 4527 (tree f102fad908) Commits folded into this step (1): - 1f23d6b61e rush-daemon-transport: stat sockets with { bigint: true } (task 210) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Pin total memory and the CPU model in the telemetry test Swarm integration step 114; original commit aa9237df9a (merge of swarm/m03-t179-nit1 at c350c17f4b). Scope: 179 + 179-nit1. Brings 179 + 179-nit1 (branch swarm/m03-t179-nit1). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 20 of 21. Gate: ch01 GATE OK board 4527 (tree 550d7d5dba) Commits folded into this step (2): - 5095b7a10d Read the CPUs once per process for rush-lib telemetry - c350c17f4b Pin total memory and the CPU model in the telemetry test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a reconciliation uses the inputs snapshot it started during the checks Swarm integration step 115; original commit f6688e3136 (merge of swarm/r07-t212-nit at 1d5e422b1b). Scope: task 212-nit. Brings 212-nit (branch swarm/r07-t212-nit). Second agents and evidence: ch01's GATE OK board 4527. s18 batch E2a, item 21 of 21. Gate: ch01 GATE OK board 4527 (tree b0adfe0514) Commits folded into this step (1): - 1d5e422b1b [rush-daemon] Test that a reconciliation uses the inputs snapshot it started during the checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft] Test that Heft writes an unreported error before it exits Swarm integration step 116; original commit d9386be78a (merge of swarm/r03-t200-nit1 at 0d97f5df3c). Scope: task 200-nit1. Brings 200-nit1 (branch swarm/r03-t200-nit1). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 1 of 20. Gate: ch01 GATE OK board 4947 (tree 4ee1bbecf7) Commits folded into this step (1): - 0d97f5df3c [heft] Test that Heft writes an unreported error before it exits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Give the FlagFile and LastInstallFlag tests separate temp folders Swarm integration step 117; original commit 714d3ef058 (merge of swarm/m03-t182 at 12bc691a65). Scope: task 182. Brings 182 (branch swarm/m03-t182). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 2 of 20. Gate: ch01 GATE OK board 4947 (tree c7cdb9a714) Commits folded into this step (1): - 12bc691a65 Give the FlagFile and LastInstallFlag tests separate temp folders Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that a result restored from the build cache passes an unselected dependency on to its consumers Swarm integration step 118; original commit 5f39d9cbdf (merge of swarm/r02-t223 at 25d212c4b2). Refs: task 223. Scope: task 223. Brings 223 (branch swarm/r02-t223). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 3 of 20. Gate: ch01 GATE OK board 4947 (tree d3386f237c) Commits folded into this step (3): - c8991173a6 [rush-lib] Don't write a cache entry built against a dependency that the request did not select (task 223) - 2a12018c98 [rush-lib] Test that no cobuild lock is taken for a result that can't be written (task 223) - 25d212c4b2 [rush-lib] Test that a result restored from the build cache passes an unselected dependency on to its consumers (task 223) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Probe a recorded group with kill() before scanning /proc for its members Swarm integration step 119; original commit 0f00ca502e (merge of swarm/r06-t203-int at ae0c48a095). Refs: task 203 ADDENDUM. Scope: task 203-int. Brings 203-int (branch swarm/r06-t203-int). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 4 of 20. Gate: ch01 GATE OK board 4947 (tree 4113fca76e) Commits folded into this step (3): - 3ccc8860f4 Sweep the operation groups of dead daemons that no lockfile names (task 203) - af48a3fdb8 Count a dead daemon's zombie operation groups as gone (task 203 follow-up) - ae0c48a095 Probe a recorded group with kill() before scanning /proc for its members (task 203 ADDENDUM) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Say that another client may be resetting the daemon when the start mutex is held Swarm integration step 120; original commit b8d29764ef (merge of swarm/o04-t199-nit1 at a589b2a5c8). Refs: 199 NIT 1. Scope: task 199-nit1. Brings 199-nit1 (branch swarm/o04-t199-nit1). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 5 of 20. Gate: ch01 GATE OK board 4947 (tree 9fee0d170b) Commits folded into this step (2): - 26baca5afa Stop the operations of a daemon that exited before "daemon stop --force" removes its files (task 199) - a589b2a5c8 [rush-client-core] Say that another client may be resetting the daemon when the start mutex is held (199 NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Return a request's freed memory to the OS once the daemon is idle (task 119) (and 1 more) Swarm integration step 121; original commit 444109a938 (merge of swarm/r06-t119-int2 at 70a127628b). Scope: task 119-int2. Brings 119-int2 (branch swarm/r06-t119-int2). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 6 of 20. Gate: ch01 GATE OK board 4947 (tree bacafe22cb) Commits folded into this step (2): - fe3c8d2770 [rush-daemon] Return a request's freed memory to the OS once the daemon is idle (task 119) - 34a30b3a0c [rush-daemon] Turn --expose-gc off again in the test of task 146 that takes gc (for task 119) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep test fixture commits from starting Git maintenance Swarm integration step 122; original commit 169911c461 (merge of swarm/m03-t229 at 031c68b106). Scope: task 229. Brings 229 (branch swarm/m03-t229). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 7 of 20. Gate: ch01 GATE OK board 4947 (tree 2bea4a2199) Commits folded into this step (1): - 031c68b106 Keep test fixture commits from starting Git maintenance Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft] Rename the heft change file for the watcher fix Swarm integration step 123; original commit a1c750a5a0 (merge of swarm/r03-t202-nit1 at f5145b5c84). Scope: task 202-nit1. Brings 202-nit1 (branch swarm/r03-t202-nit1). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 8 of 20. Gate: ch01 GATE OK board 4947 (tree 72f55c8f4d) Commits folded into this step (2): - 91f764e711 [heft] Close the previous watcher when a watch-mode run starts - f5145b5c84 Rename the heft change file for the watcher fix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that the incremental guard skips files Git ignores Swarm integration step 124; original commit a711e0cd22. Brings 190-nit1 (no branch in the shared repo, merged by hash). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 9 of 20. Gate: ch01 GATE OK board 4947 (tree 2ad2121399) Commits folded into this step (2): - c7eb2ac872 Check whether input files change while an operation runs, cache or not - 99d972ceef [rush-lib] Test that the incremental guard skips files Git ignores Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Make OrphanReapOnReclaim.test's operation child exit by itself Swarm integration step 125; original commit 1ddbf04929 (merge of swarm/r06-t237 at 40a7184719). Refs: task 237. Scope: task 237. Brings 237 (branch swarm/r06-t237). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 10 of 20. Gate: ch01 GATE OK board 4947 (tree f9bc3b79f2) Commits folded into this step (1): - 40a7184719 [rush-daemon-transport] Make OrphanReapOnReclaim.test's operation child exit by itself (task 237) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test that pongs keep a long request from counting as silent (task 69(a), o01 NIT 1) Swarm integration step 126; original commit 1348edae77 (merge of swarm/r04-t69 at c0ffde495b). Scope: task 69(a). Brings 69(a) (branch swarm/r04-t69). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 11 of 20. Gate: ch01 GATE OK board 4947 (tree e0a7501791) Commits folded into this step (2): - e95dedc22e Say when rushd stops responding while a command runs (task 69(a)) - c0ffde495b Test that pongs keep a long request from counting as silent (task 69(a), o01 NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Queue a watch iteration when an IPC runner requests a run Swarm integration step 127; original commit c696721977 (merge of swarm/r01-t216 at 71843caf92). Scope: task 216. Brings 216 (branch swarm/r01-t216). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 12 of 20. Gate: ch01 GATE OK board 4947 (tree 262b9b852a) Commits folded into this step (1): - 71843caf92 [rush-lib] Queue a watch iteration when an IPC runner requests a run Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Move task 214's new tests so that they merge with other batch E changes Swarm integration step 128; original commit 353f68cdae (merge of swarm/r06-t214 at 5f5b0f8f52). Scope: task 214. Brings 214 (branch swarm/r06-t214). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 13 of 20. Gate: ch01 GATE OK board 4947 (tree a340192f88) Commits folded into this step (3): - 7c2e74cc0e Stop the recorded operations of an owner whose PID was reused before removing its record (task 214) - 61e0c5f5d9 Ignore ESRCH when test cleanup kills a process that already exited (transport tests) - 5f5b0f8f52 [rush-client-core] Move task 214's new tests so that they merge with other batch E changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Let clients that are still connecting join a shared build batch (task 135) (and 1 more) Swarm integration step 129; original commit 99dcfdbff1 (merge of swarm/r05-t135-int3 at 03f9eb57c9). Scope: task 135-int3. Brings 135-int3 (branch swarm/r05-t135-int3). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 14 of 20. Gate: ch01 GATE OK board 4947 (tree 95ff3d06f0) Commits folded into this step (2): - 625686f093 rush-daemon: let clients that are still connecting join a shared build batch (task 135) - 4d3ea3d5e1 rush-daemon: a connecting client stays active until the daemon finishes writing its reply (task 135) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-lint-plugin] Fix the ESLint cache for files that have suppressed messages Swarm integration step 130; original commit 4f4c0f007a (merge of swarm/m03-t236 at e17746ecd1). Scope: task 236. Brings 236 (branch swarm/m03-t236). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 15 of 20. Gate: ch01 GATE OK board 4947 (tree d7e1301879) Commits folded into this step (1): - e17746ecd1 Fix the ESLint cache for files that have suppressed messages Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Say what a startup test's reservation pins Swarm integration step 131; original commit 1d80464c2e (merge of swarm/o04-t133-nits-c1-int at 0147e14b79). Scope: task 133-nits-c1. Brings 133-nits-c1 (branch swarm/o04-t133-nits-c1-int). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 16 of 20. Gate: ch01 GATE OK board 4947 (tree 291bfd8666) Commits folded into this step (1): - d7a0508fc6 [rush-client-core] Say what a startup test's reservation pins Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] End held test fixture scripts after 2 minutes Swarm integration step 132; original commit f1c85170b9 (merge of swarm/o05-t235 at 64aa3de608). Scope: task 235. Brings 235 (branch swarm/o05-t235). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 17 of 20. Gate: ch01 GATE OK board 4947 (tree 026d38cdd8) Commits folded into this step (1): - 64aa3de608 [rush-daemon] End held test fixture scripts after 2 minutes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test unreadable outputs, entry size and identity, and stalled digest workers Swarm integration step 133; original commit 0b59409558 (merge of swarm/r02-t82-nits at 90984603de). Refs: 82 NITs, board 3747. Scope: task 239. Brings 239 (branch swarm/r02-t82-nits). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 18 of 20. Gate: ch01 GATE OK board 4947 (tree 7f9d5dd87d) Commits folded into this step (1): - 90984603de [rush-daemon] Test unreadable outputs, entry size and identity, and stalled digest workers (82 NITs, board 3747) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test that a wait line for other variables doesn't stop the restart notice Swarm integration step 134; original commit e62983f879 (merge of swarm/r06-t222-nit1 at 7cb5585c96). Refs: task 222, NIT board 4399. Scope: task 222-nit1. Brings 222-nit1 (branch swarm/r06-t222-nit1). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 19 of 20. Gate: ch01 GATE OK board 4947 (tree ec3109b104) Commits folded into this step (1): - 7cb5585c96 [rush-cli-client] Test that a wait line for other variables doesn't stop the restart notice (task 222, NIT board 4399) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Serve the phased commands of command-line.json (task 35 step A) (and 17 more) Swarm integration step 135; original commit 4286b8f643 (merge of swarm/r02-t140-nit1-int8 at a0cefd5bb2). Scope: task 140-nit1-int8. Brings 140-nit1-int8 (branch swarm/r02-t140-nit1-int8). Second agents and evidence: ch01's GATE OK board 4947. s18 batch E2b, item 20 of 20. Gate: ch01 GATE OK board 4947 (tree dac294edad) Commits folded into this step (18): - b8db1ce0c1 [rush-daemon] Serve the phased commands of command-line.json (task 35 step A) - 6cb2fa4c66 [rush-daemon] Share one engine between phased commands (task 52) - 11d214dc2a [rush-daemon] Move the native engine test fixture into its own module - 7ffbd6518a [rush-lib] Test that an iteration that allows no incremental build runs the initial command and sets the base - ce54989d32 [rush-lib] Let declared command-agnostic plugins tap runAnyPhasedCommand without blocking engine sharing - ac925e31a4 [rush-lib] Name the parameters that differ when a daemon engine can't serve a request - f1d4370da4 [rush-lib] Test that a non-incremental iteration enables operations that a plugin found up to date - e8e6287196 Test that a shared engine logs each request's telemetry under the request's own command - 0439a3d936 Answer a custom command's usage error only as 78 answers build's (35 x 78, 35 x 167) - be67131f0f Say how the daemon answers an invalid custom command line in the change file (t06 board 3592 NIT 2) - bb334b52da Format the files of the D2 merge with Prettier - 1bb81ccb29 Reload with a graph-affecting parameter in 78 NIT 7's test, since build's engine serves --ignore-hooks (140 x D2) - f5e9c91709 Test the parse that the identity check keeps with 140's engine sharing and origin check (146 x 140) - e0612c809e Move 35's custom-command tests out of ProductionDaemonRequestResolver.test.ts (140 x 82, max-lines) - 0c5e7cd739 Keep commands that are not incremental off engines with persistent IPC runners (52 x persistentIpc) - 9c5ba56dc0 Move the RushX holder and legacy-request tests to 35's custom-command parse (ch01 board 4236) - 0ecbe7a28a Document that persistent IPC runners keep rebuild on its own graph (52 x persistentIpc) - 1eb52cd473 [rush-daemon] Test the name check of a request that reuses its identity check's parse (t06 NIT 1, board 3898) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Delete consumer skip records after an unverifiable rebuild Swarm integration step 136; original commit 456dc82be2 (merge of swarm/r03-t158-xint6 at 1786e61f4b). Scope: task 158. Brings 158-xint6 (branch swarm/r03-t158-xint6). Second agent: o07 board 5245. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 1 of 19. Gate: ch01 GATE OK board 5319 (tree 5b4f6b4837) Commits folded into this step (5): - 2f5141ae60 rush-lib: keep the legacy skip records of operations that a command doesn't execute - 192c8fb878 rush-lib: keep the legacy skip records of consumers when a rebuild reproduces their dependency's outputs - 37a7fbf66b [rush-lib] Test the LegacySkipPlugin paths that o03's mutants K1, K4, K5, K6 and K10 changed - 0933fc39f1 [rush-lib] Test that a rebuild iteration of a build engine keeps the legacy skip records of operations it doesn't execute - 1786e61f4b Delete consumer skip records after an unverifiable rebuild Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Cancel a request whose output reader exits Swarm integration step 137; original commit 2e992c3a83 (merge of swarm/o05-t226-int2 at a19724064d). Refs: task 226. Scope: task 226. Brings 226-int2 (branch swarm/o05-t226-int2). Second agent: t05 board 4768. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 2 of 19. Gate: ch01 GATE OK board 5319 (tree aa4f80a8c9) Commits folded into this step (1): - 8a7de65c9b [rush-cli-client] Cancel a request whose output reader exits (task 226) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] A native install or update whose waits span an installation change gets the typed restart (task 189) (and 3 more) Swarm integration step 138; original commit ef3d2bd48b (merge of swarm/r05-t189-int6 at decc8b6dee). Scope: task 189. Brings 189-int6 (branch swarm/r05-t189-int6). Second agent: t07 board 4949. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 3 of 19. Gate: ch01 GATE OK board 5319 (tree df6dd68523) Commits folded into this step (4): - 5b7cbf5176 rush-daemon: a native install or update whose waits span an installation change gets the typed restart (task 189) - b3980cd89b Test the four gaps that t05 found in task 189 (P2, P9, P11, Q5) - a505e7dcf9 Expect the shutdown line in the test of an install whose waits span an installation change - fa1cbb46d7 rush-cli-client: begin rushx-client's closed-output cancellation line with rushx-client Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Leave the warm set out of the reply to the liveness check's ping Swarm integration step 139; original commit d86d223354 (merge of swarm/r05-t232-int at df7f04af6d). Scope: task 232. Brings 232-int (branch swarm/r05-t232-int). Second agent: t02 board 4792. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 4 of 19. Gate: ch01 GATE OK board 5319 (tree fafcee70d6) Commits folded into this step (2): - 575611d655 [rush-daemon] Let a ping leave the warm set out of its pong - df7f04af6d [rush-client-core] Leave the warm set out of the reply to the liveness check's ping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Hash each output folder's file entries in one update (and 1 more) Swarm integration step 140; original commit c3ab5ef5bf (merge of swarm/r07-t230-int at 54ecc5ec92). Scope: task 230. Brings 230-int (branch swarm/r07-t230-int). Second agent: r02 board 4899; ch04 METRICS OK board 4844, board 4893. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 5 of 19. Gate: ch01 GATE OK board 5319 (tree 2fb262c2fd) Commits folded into this step (2): - 34c009bae0 [rush-daemon] Hash each output folder's file entries in one update - c88b76cb9c [rush-daemon] Walk the last checked output folders while the inputs are reconciled Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Reject a command that the daemon never serves before it waits for admission (task 207) (and 2 more) Swarm integration step 141; original commit 52ca3eb3c1 (merge of swarm/r04-t207-int at ec03d0f668). Scope: task 207. Brings 207-int (branch swarm/r04-t207-int). Second agent: t03 board 4878. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 6 of 19. Gate: ch01 GATE OK board 5319 (tree 1c34de4eee) Commits folded into this step (3): - a8debe7967 Reject a command that the daemon never serves before it waits for admission (task 207) - 8adbd4a9d1 Test the early rejection that 207 and 140 now share (task 207 on 140-int5) - 83573491f1 Test the table of commands that the daemon rejects before admission (task 207 on 140-int5) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Test that a held daemon keeps its ownership when a restart times out Swarm integration step 142; original commit a95c5781f7 (merge of swarm/r06-t184-nits-m4 at b3b25ac539). Refs: task 184, t05's P3 NIT. Scope: task 184. Brings 184-nits-m4 (branch swarm/r06-t184-nits-m4). Second agent: t05 board 5138. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 7 of 19. Gate: ch01 GATE OK board 5319 (tree f7a5ebb16c) Commits folded into this step (3): - f928ed0c26 [rush-daemon] Escape format and separator characters and backslashes in restart variable names (task 184 NITs 1 and 2) - e5fa44afd3 [rush-client-core] Test that a failed restart keeps a timeout error's code (task 184 NIT 4) - b3b25ac539 [rush-client-core] Test that a held daemon keeps its ownership when a restart times out (task 184, t05's P3 NIT) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-webpack5-plugin] Wait for pending watch events before a flush Swarm integration step 143; original commit 0e688b8284 (merge of swarm/r03-t217 at 731764213b). Scope: task 217. Brings 217 (branch swarm/r03-t217). Second agent: o08 board 4985, board 4986. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 8 of 19. Gate: ch01 GATE OK board 5319 (tree 27f9e04d2e) Commits folded into this step (1): - 731764213b [heft-webpack5-plugin] Wait for pending watch events before a flush Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Queue a watch iteration for edits made during an iteration Swarm integration step 144; original commit b7520b9276 (merge of swarm/r01-t243 at b6e79d10e7). Scope: task 243. Brings 243 (branch swarm/r01-t243). Second agent: t02 board 5011, board 5013. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 9 of 19. Gate: ch01 GATE OK board 5319 (tree b0ac566070) Commits folded into this step (1): - b6e79d10e7 [rush-lib] Queue a watch iteration for edits made during an iteration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Check input files for skip detection when the build cache is off Swarm integration step 145; original commit 7b053b8dd2 (merge of swarm/m03-t233-int2 at 503e0c4eab). Scope: task 233. Brings 233-int2 (branch swarm/m03-t233-int2). Second agent: o07 board 5055. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 10 of 19. Gate: ch01 GATE OK board 5319 (tree 3cc271dbd4) Commits folded into this step (1): - 4cd08ffcb4 Check input files for skip detection when the build cache is off Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Tolerate a late daemon answer in the RushX daemon test comparisons Swarm integration step 146; original commit 785c3666f6 (merge of swarm/m03-t256 at d3189ef41c). Scope: task 256. Brings 256 (branch swarm/m03-t256). Second agent: t07 board 5135. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 11 of 19. Gate: ch01 GATE OK board 5319 (tree 55a5cad877) Commits folded into this step (1): - d3189ef41c Tolerate a late daemon answer in the RushX daemon test comparisons Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-webpack5-plugin] Don't delete assets outside the output folder Swarm integration step 147; original commit 889ad40a8a (merge of o07/webpack-stale-assets at 250c9bc9fd). Scope: task 124. Brings 124 (branch refs/remotes/o07/webpack-stale-assets (by hash; no swarm/ branch)). Second agent: r03 board 5006. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 12 of 19. Gate: ch01 GATE OK board 5319 (tree 11c6a55c21) Commits folded into this step (2): - 61c37efade [heft-webpack5-plugin] Add a deleteStaleAssetsInWatchMode option - 250c9bc9fd [heft-webpack5-plugin] Don't delete assets outside the output folder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Log one line for a reply to a client that went away Swarm integration step 148; original commit 75140fe219 (merge of swarm/r03-t260 at 65241686a4). Scope: task 260. Brings 260 (branch swarm/r03-t260). Second agent: o01 board 5104. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 13 of 19. Gate: ch01 GATE OK board 5319 (tree d07dd3cea1) Commits folded into this step (1): - 65241686a4 [rush-daemon] Log one line for a reply to a client that went away Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] rush-daemon-transport tests: remove what test keys leave in the shared runtime directory Swarm integration step 149; original commit c476964f9e (merge of swarm/r06-t262 at 9f725c2448). Scope: task 262. Brings 262 (branch swarm/r06-t262). Second agent: t06 board 5200, board 5201. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 14 of 19. Gate: ch01 GATE OK board 5319 (tree b2868a110a) Commits folded into this step (1): - 9f725c2448 rush-daemon-transport tests: remove what test keys leave in the shared runtime directory Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a batched iteration announces a shared operation to each client that selects it Swarm integration step 150; original commit 519e714646 (merge of swarm/r05-t178-e2b6 at dec25391d8). Scope: task 178. Brings 178 (branch swarm/r05-t178-e2b6). Second agent: t01 board 5117. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 15 of 19. Gate: ch01 GATE OK board 5319 (tree 1bbc8f7ef6) Commits folded into this step (2): - 081fdd38f6 [rush-lib] [rush-daemon] Announce a batched iteration to each client with only its own operations - dec25391d8 [rush-daemon] Test that a batched iteration announces a shared operation to each client that selects it Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Stop a CLI command whose output reader exits, and exit 141 Swarm integration step 151; original commit 6fc577a590 (merge of swarm/o05-t254-int at 64768d793c). Scope: task 254. Brings 254-int (branch swarm/o05-t254-int). Second agent: t05 board 5108. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 16 of 19. Gate: ch01 GATE OK board 5319 (tree d7a605e510) Commits folded into this step (1): - d4926f9bc2 [rush-lib] Stop a CLI command whose output reader exits, and exit 141 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Log the operations that a starting daemon stops as one rushd line (task 177 NIT 1) (and 2 more) Swarm integration step 152; original commit af4a998c7d (merge of swarm/r06-t177-nit1-int5 at e9cc1320dc). Scope: task 177. Brings 177-nit1-int5 (branch swarm/r06-t177-nit1-int5). Second agent: t05 board 5206. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 17 of 19. Gate: ch01 GATE OK board 5319 (tree 60f4015fee) Commits folded into this step (3): - 4d151964c1 [rush-daemon] Log the operations that a starting daemon stops as one rushd line (task 177 NIT 1) - 0d0b317967 [rush-daemon-transport] Test that the listener passes on only onOrphansReaped (task 177 NIT 1) - 122c66881c [rush-daemon-transport] Pass the reclaim options in ListenerCloseFailure.test.ts (task 177 NIT 1 with task 197) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test that the daemon liveness lines go to stderr Swarm integration step 153; original commit 1458f87c91 (merge of swarm/o05-t266 at 473112d986). Scope: task 266. Brings 266 (branch swarm/o05-t266). Second agent: o02 board 5223. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 18 of 19. Gate: ch01 GATE OK board 5319 (tree d4f20ba7a7) Commits folded into this step (1): - 473112d986 [rush-cli-client] Test that the daemon liveness lines go to stderr Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Detect changes to hidden .gitattributes files in RepoStateCache Swarm integration step 154; original commit 4b33c75100 (merge of swarm/r07-t252 at ac7c00af64). Scope: task 252. Brings 252 (branch swarm/r07-t252). Second agent: t04 board 5237, board 5239. Evidence: ch01's GATE OK board 5319. s19 batch E2c, item 19 of 19. Gate: ch01 GATE OK board 5319 (tree 954570f670) Commits folded into this step (1): - ac7c00af64 [package-deps-hash] Detect changes to hidden .gitattributes files in RepoStateCache Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * Update the warm worker notes on input checks and TypeScript Swarm integration step 155; original commit 6497d8ac5e (merge of o07/daemon-doc-input-checks at 43207db591). Scope: doc, no task. Brings warm-worker doc (o07's branch o07/daemon-doc-input-checks, merged by hash; no swarm/ branch). Second agent: r01 board 5313 (o07 CLAIM board 5270). Evidence: ch01's GATE OK board 5753. Batch E2d, item 1 of 12. Gate: ch01 GATE OK board 5753 (tree 96ddab7127) Commits folded into this step (1): - 43207db591 Update the warm worker notes on input checks and TypeScript Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-webpack5-plugin] Explain when a relative path is absolute Swarm integration step 156; original commit 6b3fd67719 (merge of o07/webpack-stale-assets-nit1 at cbf3f17a00). Scope: task 124. Brings 124 comment NIT (o07's branch o07/webpack-stale-assets-nit1, merged by hash; no swarm/ branch). Second agent: r03 DIFF CHECK OK board 5071. Evidence: ch01's GATE OK board 5753. Batch E2d, item 2 of 12. Gate: ch01 GATE OK board 5753 (tree 87468e480a) Commits folded into this step (1): - cbf3f17a00 [heft-webpack5-plugin] Explain when a relative path is absolute Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that watch mode finds edits made during an iteration Swarm integration step 157; original commit 8327c99f3a (merge of swarm/r01-t268 at 3d68091385). Scope: task 268. Brings 268 (branch swarm/r01-t268). Second agent: t02 board 5336, board 5394. Evidence: ch01's GATE OK board 5753. Batch E2d, item 3 of 12. Gate: ch01 GATE OK board 5753 (tree ead63a020e) Commits folded into this step (1): - 3d68091385 [rush-lib] Test that watch mode finds edits made during an iteration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-webpack5-plugin] Tests hold the thread pool until the flush waits Swarm integration step 158; original commit b4eee6082e (merge of swarm/r03-t217-nit1 at e0bb8a652d). Scope: task 217. Brings 217-nit1 (branch swarm/r03-t217-nit1). Second agent: o07 board 5456. Evidence: ch01's GATE OK board 5753. Batch E2d, item 4 of 12. Gate: ch01 GATE OK board 5753 (tree d86209aee4) Commits folded into this step (1): - e0bb8a652d [heft-webpack5-plugin] Tests hold the thread pool until the flush waits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-rspack-plugin] Wait for pending watch events in the Rspack and Webpack 4 plugins Swarm integration step 159; original commit 80b28226f8 (merge of swarm/r01-t244 at f7de575da0). Scope: task 244. Brings 244 (branch swarm/r01-t244). Second agent: o08 board 5409. Evidence: ch01's GATE OK board 5753. Batch E2d, item 5 of 12. Gate: ch01 GATE OK board 5753 (tree d95353ddd1) Commits folded into this step (1): - f7de575da0 Wait for pending watch events in the Rspack and Webpack 4 plugins Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [node-core-library] Don't run ps for the lockfile of a process that exited on Linux Swarm integration step 160; original commit dbab9e1822 (merge of swarm/r07-t263-int at 750bb1e56d). Scope: task 263. Brings 263-int (branch swarm/r07-t263-int). Second agent: t06 board 5471, board 5474. Evidence: ch01's GATE OK board 5753. Batch E2d, item 6 of 12. Gate: ch01 GATE OK board 5753 (tree 56932ba7f5) Commits folded into this step (1): - 750bb1e56d [node-core-library] Don't run ps for the lockfile of a process that exited on Linux Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Run Rush in-process when auto-start cannot open the launcher log Swarm integration step 161; original commit 7f89cc400b (merge of swarm/o04-t224-int2 at 33ddeaaf78). Scope: task 224. Brings 224-int2 (branch swarm/o04-t224-int2). Second agent: t02 board 4628. Evidence: ch01's GATE OK board 5753. Batch E2d, item 7 of 12. Gate: ch01 GATE OK board 5753 (tree 36315772b9) Commits folded into this step (1): - 7ec7752565 Run Rush in-process when auto-start cannot open the launcher log Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Record each operation's outputs when Rush reports its result Swarm integration step 162; original commit 38ada69ef7 (merge of swarm/r02-t251-int at 8fcf4a55aa). Scope: task 251. Brings 251-int (branch swarm/r02-t251-int). Second agent: ch05 board 5345, board 5406. Evidence: ch01's GATE OK board 5753. Batch E2d, item 8 of 12. Gate: ch01 GATE OK board 5753 (tree ec6cff2ae1) Commits folded into this step (1): - ec8dcf7bc9 [rush-daemon] Record each operation's outputs when Rush reports its result Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon][rush-lib] Test four behaviors of task 140 that no test pinned Swarm integration step 163; original commit 35fdea784b (merge of swarm/r02-t267 at 958191312e). Refs: task 267. Scope: task 267. Brings 267 (branch swarm/r02-t267). Second agent: o07 board 5444. Evidence: ch01's GATE OK board 5753. Batch E2d, item 9 of 12. Gate: ch01 GATE OK board 5753 (tree c0fb0703ac) Commits folded into this step (1): - 958191312e [rush-daemon][rush-lib] Test four behaviors of task 140 that no test pinned (task 267) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Wait for another Rush process's repository lock within the request's wait timeout, and name it (task 111) (and 2 more) Swarm integration step 164; original commit e281b12d5c (merge of swarm/o04-t111-e2d at 5e8059a11f). Scope: task 111. Brings 111-e2d (branch swarm/o04-t111-e2d). Second agent: t05 board 5542. Evidence: ch01's GATE OK board 5753. Batch E2d, item 10 of 12. Gate: ch01 GATE OK board 5753 (tree cf7a0c604a) Commits folded into this step (3): - 5effde9755 Wait for another Rush process's repository lock within the request's wait timeout, and name it (task 111) - 64a587e85a Test the daemon's own lock, a client that leaves a waiting batch, and requests behind a waiting load (task 111) - b805c189cf Move the admission validation into DaemonRequestAdmissionValidation.ts (task 111) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-sdk] Update the snapshot of rush-lib's exported names Swarm integration step 165; original commit fc3bbd4be6 (merge of swarm/r01-t282-int at 4d0a5c5ec1). Scope: task 282. Brings 282 (branch swarm/r01-t282-int). Second agent: t02 board 5453. Evidence: ch01's GATE OK board 5753. Batch E2d, item 11 of 12. Gate: ch01 GATE OK board 5753 (tree 0b0bbf6b41) Commits folded into this step (1): - 4d0a5c5ec1 [rush-sdk] Update the snapshot of rush-lib's exported names Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Connect as soon as the startup helper exits Swarm integration step 166; original commit 86aaa686ff (merge of swarm/r07-t265 at d37f03a1a9). Scope: task 265. Brings 265 (branch swarm/r07-t265). Second agent: ch03 board 5514, board 5516. Evidence: ch01's GATE OK board 5753. Batch E2d, item 12 of 12. Gate: ch01 GATE OK board 5753 (tree 0eba3f01f4) Commits folded into this step (1): - d37f03a1a9 [rush-client-core] Connect as soon as the startup helper exits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Connect as soon as another client's startup reservation is released Swarm integration step 167; original commit 89f0590d86 (merge of swarm/r07-t281 at 9d4f376c20). Scope: task 281. Brings 281 (branch swarm/r07-t281). Second agent: t01 board 5657. Evidence: ch01's GATE OK board 6389. Batch E2e, item 1 of 17. Gate: ch01 GATE OK board 6389 (tree 69667597c6) Commits folded into this step (1): - 9d4f376c20 [rush-client-core] Connect as soon as another client's startup reservation is released Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Poll for daemon readiness every 50 ms at startup Swarm integration step 168; original commit 9c7783b122 (merge of o07/startup-helper-readiness-poll at 6a721681d2). Scope: task 285. Brings 285 (branch o07/startup-helper-readiness-poll). Second agent: t01 board 5736; o07 accepts board 5770. Evidence: ch01's GATE OK board 6389. Batch E2e, item 2 of 17. Gate: ch01 GATE OK board 6389 (tree 741b07706e) Commits folded into this step (1): - 6a721681d2 [rush-client-core] Poll for daemon readiness every 50 ms at startup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] rush-daemon tests: stop successor daemons after each test, including one that timed out, and remove their logs Swarm integration step 169; original commit c74de5ea69 (merge of swarm/r06-t246 at 727f48498c). Scope: task 246. Brings 246 (branch swarm/r06-t246). Second agent: o05 board 5671. Evidence: ch01's GATE OK board 6389. Batch E2e, item 3 of 17. Gate: ch01 GATE OK board 6389 (tree f4c767292c) Commits folded into this step (1): - 727f48498c rush-daemon tests: stop successor daemons after each test, including one that timed out, and remove their logs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that the resolver rejects a command it never serves before it parses Swarm integration step 170; original commit 844b73b77b (merge of swarm/r02-t270-int3 at 14813ec37f). Refs: task 270 on 207. Scope: task 270. Brings 270 (branch swarm/r02-t270-int3). Second agent: r01 board 5473. Evidence: ch01's GATE OK board 6389. Batch E2e, item 4 of 17. Gate: ch01 GATE OK board 6389 (tree 5b6a1933dc) Commits folded into this step (2): - 86ba3f2c62 [rush-daemon] Restart for a phased custom command whose environment differs - 14813ec37f [rush-daemon] Test that the resolver rejects a command it never serves before it parses (task 270 on 207) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Note the sparse checkout cost of hidden attributes Swarm integration step 171; original commit f018ef978e (merge of swarm/r07-t252-nit1 at 8d2e3406a6). Scope: task 252, NIT 1. Brings 252-nit1 (branch swarm/r07-t252-nit1). Second agent: t04 board 5680. Evidence: ch01's GATE OK board 6389. Batch E2e, item 5 of 17. Gate: ch01 GATE OK board 6389 (tree 6d6c2d6524) Commits folded into this step (1): - 8d2e3406a6 [package-deps-hash] Note the sparse checkout cost of hidden attributes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-rspack-plugin] Hold the thread pool in the Rspack and Webpack 4 watch flush tests Swarm integration step 172; original commit ac1bad2b8d (merge of swarm/r01-t244-nit1 at fc6636a6bd). Scope: task 244, NIT 1. Brings 244-nit1 (branch swarm/r01-t244-nit1). Second agent: o08 board 5640. Evidence: ch01's GATE OK board 6389. Batch E2e, item 6 of 17. Gate: ch01 GATE OK board 6389 (tree 769bbeb001) Commits folded into this step (1): - fc6636a6bd Hold the thread pool in the Rspack and Webpack 4 watch flush tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Add a beforeLogRequest hook for every telemetry entry Swarm integration step 173; original commit 8cf6e7e168 (merge of swarm/r02-t284-e2e at a7a90d4780). Scope: task 284, rushstack half. Brings 284 (branch swarm/r02-t284-e2e). Second agent: m03 board 5723, carried over board 5898. Evidence: ch01's GATE OK board 6389. Batch E2e, item 7 of 17. Gate: ch01 GATE OK board 6389 (tree 1e5c84d2fa) Commits folded into this step (1): - a7a90d4780 [rush-lib] Add a beforeLogRequest hook for every telemetry entry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft] Don't start an extra watch run for a change a run already read Swarm integration step 174; original commit eb0dff2f1d (merge of swarm/r03-t277 at 2ab272b8c5). Scope: task 277. Brings 277 (branch swarm/r03-t277). Second agent: ch05 board 6037, board 6038. Evidence: ch01's GATE OK board 6389. Batch E2e, item 8 of 17. Gate: ch01 GATE OK board 6389 (tree 775c0fe226) Commits folded into this step (1): - 2ab272b8c5 [heft] Don't start an extra watch run for a change a run already read Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Say that the client-gone check remembers the error Swarm integration step 175; original commit fbc9b92a13 (merge of swarm/r03-t260-nit1 at 1848cdecf3). Scope: task 260, NIT 1. Brings 260-nit1 (branch swarm/r03-t260-nit1). Second agent: o01 board 5810. Evidence: ch01's GATE OK board 6389. Batch E2e, item 9 of 17. Gate: ch01 GATE OK board 6389 (tree 7b3d0639fb) Commits folded into this step (1): - 1848cdecf3 [rush-daemon] Say that the client-gone check remembers the error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft-webpack5-plugin] Name the Node version in a test comment Swarm integration step 176; original commit 2c1e2f1036 (merge of swarm/r03-t217-nit2 at d2e5d575e0). Scope: task 217, NIT 2. Brings 217-nit2 (branch swarm/r03-t217-nit2). Second agent: o07 board 5844. Evidence: ch01's GATE OK board 6389. Batch E2e, item 10 of 17. Gate: ch01 GATE OK board 6389 (tree 4226f399c2) Commits folded into this step (1): - d2e5d575e0 [heft-webpack5-plugin] Name the Node version in a test comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Expect "did not answer" in the rush-daemon test of a live owner that does not answer (task 69(b), row 11 fix) Swarm integration step 177; original commit 08be97eef7 (merge of swarm/r04-t69b-int3 at 09ef179921). Scope: task 69(b). Brings 69b (branch swarm/r04-t69b-int3). Second agent: r01 board 6106, board 6108. Evidence: ch01's GATE OK board 6389. Batch E2e, item 11 of 17. Gate: ch01 GATE OK board 6389 (tree a1dc0fbf8c) Commits folded into this step (3): - 3f3c496bb1 Say what a live daemon owner that does not answer is doing (task 69(b)) - dc3460dec0 Stop waiting for a daemon that a signal keeps stopped (task 69(b), b3) - 09ef179921 Expect "did not answer" in the rush-daemon test of a live owner that does not answer (task 69(b), row 11 fix) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Fail a lock-wait test that never stops trying instead of hanging Jest Swarm integration step 178; original commit 0052544761 (merge of swarm/o04-t245-nit1 at 3543843763). Scope: task 245, NIT 1. Brings 245-nit1 (branch swarm/o04-t245-nit1). Second agent: r04 board 6056. Evidence: ch01's GATE OK board 6389. Batch E2e, item 12 of 17. Gate: ch01 GATE OK board 6389 (tree 47e7bee143) Commits folded into this step (3): - 66af75ee6f Wait for the repository lock when rush-client runs a command in-process after it tried the daemon, and name the holder (task 245) - 6fb1d10df0 Test that an engine host leaves the lock wait alone, and keep a failing released-lock test from ending its file (task 245) - 3543843763 Fail a lock-wait test that never stops trying instead of hanging Jest Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Stat the socket with bigint in the reset's reclaim Swarm integration step 179; original commit d7fd156874 (merge of swarm/r01-t286-int at 2fe648fb95). Scope: task 286. Brings 286 (branch swarm/r01-t286-int). Second agent: r04 board 5816, carried over board 5841. Evidence: ch01's GATE OK board 6389. Batch E2e, item 13 of 17. Gate: ch01 GATE OK board 6389 (tree d23bf8ab05) Commits folded into this step (1): - 2fe648fb95 [rush-client-core] Stat the socket with bigint in the reset's reclaim Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Tie a leaderless operation group to its daemon Swarm integration step 180; original commit e4b8c90ad1 (merge of swarm/r06-t248-int at bc8c24c0d6). Scope: task 248. Brings 248 (branch swarm/r06-t248-int). Second agent: t05 board 6010. Evidence: ch01's GATE OK board 6389. Batch E2e, item 14 of 17. Gate: ch01 GATE OK board 6389 (tree 3817b624bc) Commits folded into this step (1): - 5902e05327 rush-daemon-transport: tie a leaderless operation group to its daemon Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] [rush-daemon] Say which command sets each differing parameter Swarm integration step 181; original commit 391fd82eae (merge of swarm/r02-t280-int at a2f90aa6bb). Scope: task 280. Brings 280 (branch swarm/r02-t280-int). Second agent: o08 board 5915, board 5974. Evidence: ch01's GATE OK board 6389. Batch E2e, item 15 of 17. Gate: ch01 GATE OK board 6389 (tree bd832486ca) Commits folded into this step (1): - 98877816c0 [rush-lib] [rush-daemon] Say which command sets each differing parameter Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Hash inputs saved near the snapshot in process before Git Swarm integration step 182; original commit 8fd4f36a23 (merge of swarm/r07-t288 at 3378c1d80e). Scope: task 288. Brings 288 (branch swarm/r07-t288). Second agent: m03 board 5994, board 5995. Evidence: ch01's GATE OK board 6389. Batch E2e, item 16 of 17. Gate: ch01 GATE OK board 6389 (tree aa48fefa04) Commits folded into this step (1): - 3378c1d80e [rush-lib] Hash inputs saved near the snapshot in process before Git Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Add tests for the record stage, lock retry delay and restart waits Swarm integration step 183; original commit cbaf4d0e86 (merge of swarm/r03-t296 at 139fbbfed2). Scope: task 296. Brings 296 (branch swarm/r03-t296). Second agent: ch03 board 6100, board 6101. Evidence: ch01's GATE OK board 6389. Batch E2e, item 17 of 17. Gate: ch01 GATE OK board 6389 (tree 5e2dc25715) Commits folded into this step (1): - 139fbbfed2 Add tests for the record stage, lock retry delay and restart waits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Make a startup helper test deterministic Swarm integration step 184; original commit 8b6f28ce62 (merge of swarm/r07-t265-nit1 at 95567746a8). Scope: task 265, NIT 1. Brings 265-nit1 (branch swarm/r07-t265-nit1). Second agent: o07 board 6628, board 6632. Evidence: ch01's GATE OK board 6977. Batch E2f, item 1 of 15. Gate: ch01 GATE OK board 6977 (tree 2c32bc7443) Commits folded into this step (1): - 95567746a8 [rush-client-core] Make a startup helper test deterministic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [heft] Add watch tests for an older mtime and a folder a run didn't read Swarm integration step 185; original commit 438ca1c7b5 (merge of swarm/r03-t277-nit1 at 1eddd88d84). Scope: task 277, NIT 1. Brings 277-nit1 (branch swarm/r03-t277-nit1). Second agent: ch05 board 6243. Evidence: ch01's GATE OK board 6977. Batch E2f, item 2 of 15. Gate: ch01 GATE OK board 6977 (tree e522b4c5e1) Commits folded into this step (1): - 1eddd88d84 [heft] Add watch tests for an older mtime and a folder a run didn't read Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Start a served script while a reload waits for a build Swarm integration step 186; original commit f47e9b019f (merge of swarm/r07-t290 at 8e82b1f9fc). Scope: task 290. Brings 290 (branch swarm/r07-t290). Second agent: t03 board 6281. Evidence: ch01's GATE OK board 6977. Batch E2f, item 3 of 15. Gate: ch01 GATE OK board 6977 (tree ba7ed8ae40) Commits folded into this step (1): - 8e82b1f9fc [rush-daemon] Start a served script while a reload waits for a build Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Expect no lock holder PID where the platform cannot tell it Swarm integration step 187; original commit 69787dffd8 (merge of swarm/o04-t302-e2f-nit3 at cf7a0237e8). Scope: task 302 + NIT 3. Brings 302-nit3 (branch swarm/o04-t302-e2f-nit3). Second agent: ch05 board 6776; ADDENDUM 3 CONFIRMED board 6814, board 6815. Evidence: ch01's GATE OK board 6977. Batch E2f, item 4 of 15. Gate: ch01 GATE OK board 6977 (tree 90adcaf963) Commits folded into this step (4): - a00afd9851 [rush-daemon] Let served rushx scripts pass a reload that waits for another Rush process (task 302) - b2565319fb [rush-daemon] Test a served rushx script while a new daemon's first build waits for another Rush process (task 302) - ec4000d620 Cover a passed rushx script that is handed back before it starts - cf7a0237e8 Expect no lock holder PID where the platform cannot tell it Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Run the initial command after a success with warnings that fail the build Swarm integration step 188; original commit 3679a012f2 (merge of swarm/r02-t298 at 52823aeb65). Refs: task 298. Scope: task 298. Brings 298 (branch swarm/r02-t298). Second agent: t07 board 6266. Evidence: ch01's GATE OK board 6977. Batch E2f, item 5 of 15. Gate: ch01 GATE OK board 6977 (tree 949aea6f61) Commits folded into this step (1): - 52823aeb65 [rush-lib] Run the initial command after a success with warnings that fail the build (task 298) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test the verdict's client key and installer failures Swarm integration step 189; original commit 8c21e38527 (merge of swarm/o05-t59-nit1 at f783a096f8). Scope: task 59 a1 + NIT. Brings 59-nit1 (branch swarm/o05-t59-nit1). Second agent: o08 board 6377, board 6740. Evidence: ch01's GATE OK board 6977. Batch E2f, item 6 of 15. Gate: ch01 GATE OK board 6977 (tree 0dd062096f) Commits folded into this step (3): - dd46f9a942 [rush-daemon] Remember why no selected daemon can serve this client - c0f519ddb0 [rush-daemon] Keep cached-only selection independent of the verdict - f783a096f8 [rush-daemon] Test the verdict's client key and installer failures Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Expect only the live-owner message in NativeMutationCleanupFailure (69(b), r01's V6 NIT) Swarm integration step 190; original commit cd5bf15ec1 (merge of swarm/r04-t69b-nit6-e2f at b728c792cc). Scope: task 69(b), NIT 6. Brings 69b-nit6 (branch swarm/r04-t69b-nit6-e2f). Second agent: r01 board 6312, r02 board 6351. Evidence: ch01's GATE OK board 6977. Batch E2f, item 7 of 15. Gate: ch01 GATE OK board 6977 (tree 71453abc20) Commits folded into this step (1): - b728c792cc Expect only the live-owner message in NativeMutationCleanupFailure (69(b), r01's V6 NIT) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Give the parser's lock-wait tests their own timeout and attempt cap Swarm integration step 191; original commit bae1e98076 (merge of swarm/o04-t245-nit2 at 10f1b94a5d). Scope: task 245, NIT 2. Brings 245-nit2 (branch swarm/o04-t245-nit2). Second agent: r04 board 6738, board 6739. Evidence: ch01's GATE OK board 6977. Batch E2f, item 8 of 15. Gate: ch01 GATE OK board 6977 (tree 25f63bd371) Commits folded into this step (2): - bcee03a5a9 Format the lock-wait lines that Prettier 3.6.2 would rewrap - 10f1b94a5d Give the parser's lock-wait tests their own timeout and attempt cap Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Wait 1 s, not 5, for an exited daemon to be reaped, and say the 15 s wait beside it Swarm integration step 192; original commit 5e2426928b (merge of swarm/r04-t297-e2f at bf10278489). Refs: task 297, item 2. Scope: task 297. Brings 297 (branch swarm/r04-t297-e2f). Second agent: r02 board 6476. Evidence: ch01's GATE OK board 6977. Batch E2f, item 9 of 15. Gate: ch01 GATE OK board 6977 (tree 9e0fc767bc) Commits folded into this step (5): - f4f37dc46b Stop waiting for a stopped daemon to exit in `daemon stop` (task 297, item 4) - 30390aa788 Say that each command waits 15 s beside a live owner that is not proven (task 297, item 1) - c8b0aabcf9 Put "Rush was not run in-process" on a line of its own beside a live daemon owner (task 297, item 3) - 80143129d9 Say when the Rush process that holds the repository lock is stopped (task 297, item 5) - bf10278489 Wait 1 s, not 5, for an exited daemon to be reaped, and say the 15 s wait beside it (task 297, item 2) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test a blocked verified skip in a later iteration Swarm integration step 193; original commit 561aa4396b (merge of swarm/r01-t303-nit1 at 8bd2a79c01). Refs: task 303. Scope: task 303 + NIT 1. Brings 303-nit1 (branch swarm/r01-t303-nit1). Second agent: o02 board 6490, board 6644, board 6648. Evidence: ch01's GATE OK board 6977. Batch E2f, item 10 of 15. Gate: ch01 GATE OK board 6977 (tree 242fb8a163) Commits folded into this step (2): - 6e5e31539a rush-lib: trust plugin skips verified against the build cache (task 303) - 8bd2a79c01 rush-lib: test a blocked verified skip in a later iteration (task 303) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that a build cache receipt checks the archived files and .rush/temp Swarm integration step 194; original commit 75fd2a1ee3 (merge of swarm/r03-t128-nit1 at cfdc0e45a2). Scope: task 128 + NIT 1. Brings 128-nit1 (branch swarm/r03-t128-nit1). Second agent: t06 board 6570, board 6707. Evidence: ch01's GATE OK board 6977. Batch E2f, item 11 of 15. Gate: ch01 GATE OK board 6977 (tree 60218ceaf1) Commits folded into this step (2): - 3f735ef49f Skip a build cache restore when the outputs already match the entry - cfdc0e45a2 Test that a build cache receipt checks the archived files and .rush/temp Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep the lock holder's command when it exits Swarm integration step 195; original commit 103bf2ec75 (merge of swarm/r04-t306-e2f at 76ad7be8c9). Scope: task 306. Brings 306 (branch swarm/r04-t306-e2f). Second agent: t07 board 6624, board 6627. Evidence: ch01's GATE OK board 6977. Batch E2f, item 12 of 15. Gate: ch01 GATE OK board 6977 (tree 013f639c5f) Commits folded into this step (2): - 1320c7daa0 [rush-cli-client] Do not write another lock-wait line when the lock holder exits - 76ad7be8c9 [rush-daemon] Keep the lock holder's command when it exits Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Fix the note on a restarted daemon's descriptors Swarm integration step 196; original commit 23ae5f9f33 (merge of swarm/r06-t299-e2f at af7ff177de). Refs: task 299. Scope: task 299. Brings 299 (branch swarm/r06-t299-e2f). Second agent: t07 board 6547, board 6548. Evidence: ch01's GATE OK board 6977. Batch E2f, item 13 of 15. Gate: ch01 GATE OK board 6977 (tree 71168d5c10) Commits folded into this step (3): - d7d3f44d2a [rush-client-core] Close inherited descriptors before starting the daemon (task 299) - 7952ac60de [rush-client-core] Note that an earlier release's daemon keeps what it inherited (task 299) - af7ff177de [rush-client-core] Fix the note on a restarted daemon's descriptors (task 299) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-protocol] Send a queued request to a new daemon once if rushd exits first Swarm integration step 197; original commit 2c271c9660 (merge of o07/t145-queued-request-resend at 8057e41829). Scope: task 145. Brings 145 (branch o07/t145-queued-request-resend). Second agent: t05 board 6718, board 6719. Evidence: ch01's GATE OK board 6977. Batch E2f, item 14 of 15. Gate: ch01 GATE OK board 6977 (tree ebea9196f8) Commits folded into this step (1): - 8057e41829 Send a queued request to a new daemon once if rushd exits first Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Capture input file states only for operations that execute (and 2 more) Swarm integration step 198; original commit 618be626ae (merge of swarm/m03-t257-int3 at d4e7adc6fb). Scope: task 257. Brings 257-int3 (branch swarm/m03-t257-int3). Second agent: t07 board 6676, board 6678. Evidence: ch01's GATE OK board 6977. Batch E2f, item 15 of 15. Gate: ch01 GATE OK board 6977 (tree 898e931022) Commits folded into this step (3): - 0f91f58740 Capture input file states only for operations that execute - 951d97e062 rush-lib: list only files in the working tree in the legacy skip test snapshot - deca9daa46 rush-lib: add the shrinkwrap file that the CLI test snapshots list to the npm test repos Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Tell a request queued at daemon shutdown that it did not start Swarm integration step 199; original commit 51bd340a56 (merge of swarm/o08-t309 at 4a2e95ea3c). Scope: task 309. Brings 309 (branch swarm/o08-t309). Second agent: o07 board 6862, board 6865. Evidence: ch01's GATE OK board 7438. Batch E2g, item 1 of 7. Gate: ch01 GATE OK board 7438 (tree 03771baa22) Commits folded into this step (1): - 4a2e95ea3c Tell a request queued at daemon shutdown that it did not start Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-client-core] Say which operations a reclaim stopped in its launcher log line Swarm integration step 200; original commit cf0bc8a3ce (merge of swarm/r06-t300-on297-nit1 at ad1ebe0b23). Refs: task 300, NIT 1. Scope: task 300 + NIT 1. Brings 300 (branch swarm/r06-t300-on297-nit1). Second agent: t05 board 6835 (de6c76f553); NIT 1: t05 board 7158 (RESULT board 7157). Evidence: ch01's GATE OK board 7438. Batch E2g, item 2 of 7. Gate: ch01 GATE OK board 7438 (tree bc90c6f26e) Commits folded into this step (4): - 55b82a157b [rush-daemon-transport] Report the operation groups that a reclaim leaves running (task 300) - 8d1e196cf3 [rush-daemon-transport] Test the order of the reasons for a group left running (task 300) - de6c76f553 [rush-daemon-transport] Judge a group left running on the proof's own reads (task 300) - ad1ebe0b23 [rush-client-core] Say which operations a reclaim stopped in its launcher log line (task 300, NIT 1) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep the daemon after a served install or update that fails before it changes the installation Swarm integration step 201; original commit 9df2179034 (merge of swarm/r02-t208 at ead3750652). Scope: task 208. Brings 208 (branch swarm/r02-t208). Second agent: t03 board 6959, board 6961. Evidence: ch01's GATE OK board 7438. Batch E2g, item 3 of 7. Gate: ch01 GATE OK board 7438 (tree a3f30dea9d) Commits folded into this step (1): - ead3750652 [rush-daemon] Keep the daemon after a served install or update that fails before it changes the installation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Restore build cache outputs through a staging folder Swarm integration step 202; original commit 74aeb1615d (merge of swarm/r03-t128c at 59a064cb92). Scope: task 128c. Brings 128c (branch swarm/r03-t128c). Second agent: t06 board 6769. Evidence: ch01's GATE OK board 7438. Batch E2g, item 4 of 7. Gate: ch01 GATE OK board 7438 (tree 6a961f3219) Commits folded into this step (1): - 59a064cb92 Restore build cache outputs through a staging folder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Name the lock holder to requests behind a reload Swarm integration step 203; original commit af484f8223 (merge of swarm/o04-t308 at baccc0ddd6). Scope: task 308. Brings 308 (branch swarm/o04-t308). Second agent: ch05 board 7122, board 7124. Evidence: ch01's GATE OK board 7438. Batch E2g, item 5 of 7. Gate: ch01 GATE OK board 7438 (tree 990e6bd793) Commits folded into this step (1): - baccc0ddd6 [rush-daemon] Name the lock holder to requests behind a reload Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Let a long-lived operation graph extend its current iteration with the work of a later request (and 4 more) Swarm integration step 204; original commit 026d62c061 (merge of swarm/r05-t109-e2g at 03aa040779). Scope: task 109c, re-cut on E2f. Brings 109c (branch swarm/r05-t109-e2g). Second agent: NB2: ch03 board 6543, board 6544; R: ch03 board 7138, board 7140. Evidence: ch01's GATE OK board 7438. Batch E2g, item 6 of 7. Gate: ch01 GATE OK board 7438 (tree f19307a5fa) Commits folded into this step (5): - a078bc9a79 [rush-lib] Let a long-lived operation graph extend its current iteration with the work of a later request - 26c7432a3d [rush-lib] Correct the docs of tryExtendCurrentIteration and the iteration hooks - 0b70aca357 [rush-daemon] Let a build request join the iteration of a running batch - 3973fbed9a [rush-lib] Add the daemon setting joinRunningBatch - 38f99a928d [rush-daemon] Read the join setting from the daemon configuration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that an exclusive request does not join an iteration Swarm integration step 205; original commit e76300af14 (merge of swarm/r05-t109-e2g-312 at 0692d91ecd). Scope: task 312. Brings 312 (branch swarm/r05-t109-e2g-312). Second agent: ch03 board 6843, board 6845 (d90db35917, same patch-ids); LG7 on S: ch03 board 7138, board 7140; 109 on S: t03 board 7153 (RESULT board 7152). Evidence: ch01's GATE OK board 7438. Batch E2g, item 7 of 7. Gate: ch01 GATE OK board 7438 (tree fd51b6802b) Commits folded into this step (3): - 92874a3e5c [rush-lib] Add beforeCommit to the options of tryExtendCurrentIteration - 56d1f994c8 [rush-daemon] Start a joining request only once the graph takes its work - 0692d91ecd [rush-daemon] Test that an exclusive request does not join an iteration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Move the request selection helpers into PhasedRequestSelection.ts Swarm integration step 206; original commit 2c09254c3a (merge of swarm/r04-t108-e2h at 5e8a18aa72). Scope: task 108, re-cut on E2g: M2, the move, on M1. Brings 108 (branch swarm/r04-t108-e2h, 9 commits). Second agent: o07 board 7594 (RESULT board 7591, CLAIM board 7528); H: o02 board 7313, board 7314; M: o02 board 7507; S: o07 board 7276, board 7277. Evidence: ch01's GATE OK board 8015. Batch E2h, item 1 of 13. Gate: ch01 GATE OK board 8015 (tree 0e8ed501c5) Commits folded into this step (7): - 96561e90c6 [rush-cli-client] Name the operations that continue in rushd after an early failure - 1230432c97 [rush-daemon-protocol] Add the operations that continue after an early result to queue positions and the workspace status - 676ebc82b4 [rush-daemon] Tell a waiting build and daemon status which operations continue after an early result - dc47ce00f9 [rush-cli-client] Name the operations that a queued request waits for after an earlier failed command - bc41a6ba62 [rush-daemon] Say which operations the daemon stopped before a command runs in-process - a6158359d9 [rush-cli-client] Say that a resubmitted command no longer waits in the exited daemon's queue - 5e8a18aa72 [rush-daemon] Move the request selection helpers into PhasedRequestSelection.ts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Say resubmitted, not the lock wait, after a restart Swarm integration step 207; original commit 26b0b98634 (merge of swarm/r04-t108-nit1 at f9834f79a8). Scope: task 108 NIT 1. Brings 108n1 (branch swarm/r04-t108-nit1, 8 commits). Second agent: o04 board 7693 (RESULT board 7691, REGISTRATION board 7577). Evidence: ch01's GATE OK board 8015. Batch E2h, item 2 of 13. Gate: ch01 GATE OK board 8015 (tree 3af003a971) Commits folded into this step (1): - f9834f79a8 [rush-cli-client] Say resubmitted, not the lock wait, after a restart Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Print the usage of an invalid command line in legacy output, as native Rush does Swarm integration step 208; original commit 0a94e2ec8d (merge of swarm/r04-t78-nit8 at 35ceae793c). Refs: task 78 NIT 8, ch01 board 3099. Scope: task 78 NIT 8. Brings 78n8 (branch swarm/r04-t78-nit8). Second agent: o07 board 7420 (RESULT board 7418). Evidence: ch01's GATE OK board 8015. Batch E2h, item 3 of 13. Gate: ch01 GATE OK board 8015 (tree bdf4c95348) Commits folded into this step (1): - 35ceae793c Print the usage of an invalid command line in legacy output, as native Rush does (task 78 NIT 8, ch01 board 3099) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that the lock holder's command is sent once it can be read Swarm integration step 209; original commit e9bdc07fa6 (merge of swarm/r04-t306-nit1 at 04f4db946a). Scope: task 306 NIT 1. Brings 306n1 (branch swarm/r04-t306-nit1). Second agent: t07 board 7372 (RESULT board 7371, CORRECTION 1 board 7377). Evidence: ch01's GATE OK board 8015. Batch E2h, item 4 of 13. Gate: ch01 GATE OK board 8015 (tree f786d14e84) Commits folded into this step (1): - 04f4db946a [rush-daemon] Test that the lock holder's command is sent once it can be read Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Resend a queued request only when every daemon frame was read Swarm integration step 210; original commit be7f7485f3 (merge of o07/t310-e2h at 16cf7ebb8a). Scope: task 310, F' on 309's ^2 (CLAIM board 7599). Brings 310 (branch o07/t310-e2h). Second agent: r02 board 7727 (RESULT board 7726; on F: board 7312). Evidence: ch01's GATE OK board 8015. Batch E2h, item 5 of 13. Gate: ch01 GATE OK board 8015 (tree d7c728ee08) Commits folded into this step (1): - 16cf7ebb8a Resend a queued request only when every daemon frame was read Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-protocol] Tell a request queued behind a script wait what it waits for Swarm integration step 211; original commit fbbd1fd00b (merge of swarm/o08-t314 at 7a9b086c6b). Scope: task 314, F2. Brings 314 (branch swarm/o08-t314). Second agent: ch05 board 7652 (RESULT board 7651, CLAIM board 7514). Evidence: ch01's GATE OK board 8015. Batch E2h, item 6 of 13. Gate: ch01 GATE OK board 8015 (tree 80545ecac7) Commits folded into this step (1): - 7a9b086c6b Tell a request queued behind a script wait what it waits for Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test which lock holder a reload names to the requests behind it Swarm integration step 212; original commit 7bc6d0fd75 (merge of swarm/o04-t308-nit-e2h at 8270530e83). Scope: task 308 test NIT, N'''. Brings 308n (branch swarm/o04-t308-nit-e2h). Second agent: ch05 board 7477, board 7491 (RESULT board 7476). Evidence: ch01's GATE OK board 8015. Batch E2h, item 7 of 13. Gate: ch01 GATE OK board 8015 (tree 32e139cb67) Commits folded into this step (1): - 8270530e83 Test which lock holder a reload names to the requests behind it Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a flag written again in place counts as a change Swarm integration step 213; original commit e67393d141 (merge of swarm/r02-t208-nit1 at e0e88b552c). Scope: task 208 NIT 1. Brings 208n1 (branch swarm/r02-t208-nit1). Second agent: t03 board 7722 (RESULT board 7721, CLAIM board 7524). Evidence: ch01's GATE OK board 8015. Batch E2h, item 8 of 13. Gate: ch01 GATE OK board 8015 (tree 0b4f28cf5e) Commits folded into this step (1): - e0e88b552c [rush-daemon] Test that a flag written again in place counts as a change Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Add tests for build cache receipt edge cases Swarm integration step 214; original commit 459772bb14 (merge of swarm/r03-t128-nit2 at eb24e5b441). Scope: task 128 NIT 2, N2. Brings 128n2 (branch swarm/r03-t128-nit2). Second agent: t06 board 7306 (RESULT board 7305). Evidence: ch01's GATE OK board 8015. Batch E2h, item 9 of 13. Gate: ch01 GATE OK board 8015 (tree b149078441) Commits folded into this step (1): - eb24e5b441 Add tests for build cache receipt edge cases Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Add tests for build cache restores through a staging folder Swarm integration step 215; original commit 40da39b4ac (merge of swarm/r03-t128c-nit1 at 5596d1629f). Scope: task 128c NIT 1, N3. Brings 128cn1 (branch swarm/r03-t128c-nit1). Second agent: t06 board 7498 (RESULT board 7496). Evidence: ch01's GATE OK board 8015. Batch E2h, item 10 of 13. Gate: ch01 GATE OK board 8015 (tree 8454ca1471) Commits folded into this step (1): - 5596d1629f Add tests for build cache restores through a staging folder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that a join leaves an executing operation with the snapshot of the iteration Swarm integration step 216; original commit 3961c18ba0 (merge of swarm/r05-t109-nit1-e2h at aa2804e9a5). Scope: task 109 NIT 1, landing form L. Brings 109n1 (branch swarm/r05-t109-nit1-e2h, 2 commits). Second agent: ch03 board 7605 (RESULT board 7603, CLAIM board 7506). Evidence: ch01's GATE OK board 8015. Batch E2h, item 11 of 13. Gate: ch01 GATE OK board 8015 (tree d0d8424e9e) Commits folded into this step (1): - 6e75773b93 [rush-lib] Test that a join leaves an executing operation with the snapshot of the iteration Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test that a join captures input files from the start of the joining snapshot Swarm integration step 217; original commit eef0e8d783 (merge of swarm/r05-t109-nit2 at e4f9bddaf6). Scope: task 109 NIT 2, N2. Brings 109n2 (branch swarm/r05-t109-nit2). Second agent: ch03 board 7462 (RESULT board 7461). Evidence: ch01's GATE OK board 8015. Batch E2h, item 12 of 13. Gate: ch01 GATE OK board 8015 (tree c0a3421d80) Commits folded into this step (1): - e4f9bddaf6 [rush-lib] Test that a join captures input files from the start of the joining snapshot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Stop the output digest pool's idle workers Swarm integration step 218; original commit 5b128eca01 (merge of swarm/r06-t320 at 97a73645cd). Scope: task 320, the digest pool stops its idle workers. Brings 320 (branch swarm/r06-t320). Second agent: r04 board 7690 (RESULT board 7688, on CLAIM board 7635 as corrected by board 7665). Evidence: ch01's GATE OK board 8015. Batch E2h, item 13 of 13. Gate: ch01 GATE OK board 8015 (tree def7f5d675) Commits folded into this step (1): - 97a73645cd rush-daemon: stop the output digest pool's idle workers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Write no receipt while an older write is pending Swarm integration step 219; original commit 3b3b61e467 (merge of swarm/r07-t113-e2h at 9641dbd7ca). Scope: task 113, re-cut on E2g (form', NIT 1', G1', P'). Brings 113 (branch swarm/r07-t113-e2h, 4 commits). Second agent: t06 board 7808 (RESULT board 7807, on CLAIM board 7624); t04 board 7914; t01 board 7916; t04 on G1's E2E board 7912, board 7946, board 7949. Evidence: ch01's GATE OK board 8659. Batch E2i, item 1 of 10. Gate: ch01 GATE OK board 8659 (tree 0625039407) Commits folded into this step (4): - 65ee205ed2 [rush-lib] Write the build cache entries of daemon builds later - 7cce85675b [rush-lib] [rush-daemon] Show failed background cache writes as warnings - 6ad586843b [rush-lib] Seal slow outputs in the background - 9641dbd7ca [rush-lib] Write no receipt while an older write is pending Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Abort an engine creation at its next step Swarm integration step 220; original commit 7e03cd72f5 (merge of swarm/r01-t117-f2 at 805afc8e49). Refs: task 117. Scope: task 117 Part 1, F2. Brings 117f2 (branch swarm/r01-t117-f2). Second agent: t07 board 7768 (RESULT board 7767, D43 board 7542, on board 7240). Evidence: ch01's GATE OK board 8659. Batch E2i, item 2 of 10. Gate: ch01 GATE OK board 8659 (tree 5c5e21e47b) Commits folded into this step (1): - 805afc8e49 rush-lib: Abort an engine creation at its next step (task 117) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [operation-graph] Report VmRSS from /proc/self/status after each warm run Swarm integration step 221; original commit e11400730a (merge of swarm/r03-t324 at 472218798b). Scope: task 324, VmRSS after each warm engine. Brings 324 (branch swarm/r03-t324). Second agent: m03 board 7859 (RESULT board 7858, on CLAIM board 7820). Evidence: ch01's GATE OK board 8659. Batch E2i, item 3 of 10. Gate: ch01 GATE OK board 8659 (tree f69b632ded) Commits folded into this step (1): - 472218798b [operation-graph] Report VmRSS from /proc/self/status after each warm run Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Test more ways a lock wait precedes a restart in agent mode Swarm integration step 222; original commit 8eb0a32328 (merge of swarm/r04-t326 at 7bce6766aa). Scope: task 326, lock waits before a restart. Brings 326 (branch swarm/r04-t326). Second agent: o04 board 7794 (RESULT board 7793, on CLAIM board 7747). Evidence: ch01's GATE OK board 8659. Batch E2i, item 4 of 10. Gate: ch01 GATE OK board 8659 (tree d3e2a6d2ad) Commits folded into this step (1): - 7bce6766aa [rush-cli-client] Test more ways a lock wait precedes a restart in agent mode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a request's plugin names must match the warm engine Swarm integration step 223; original commit 01224b9c3d (merge of swarm/r04-t331 at c39dcd9b45). Scope: task 331, plugin names must match the warm engine. Brings 331 (branch swarm/r04-t331). Second agent: o04 board 7933 (RESULT board 7932, on CLAIM board 7878). Evidence: ch01's GATE OK board 8659. Batch E2i, item 5 of 10. Gate: ch01 GATE OK board 8659 (tree b824e27bda) Commits folded into this step (1): - c39dcd9b45 [rush-daemon] Test that a request's plugin names must match the warm engine Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test validateEngineShape with two phases and two plugins Swarm integration step 224; original commit 4065ad7dc1 (merge of swarm/r04-t334 at 633ea4d5e4). Scope: task 334, validateEngineShape with two phases and two plugins. Brings 334 (branch swarm/r04-t334, 2 commits). Second agent: o04 board 8141 (RESULT board 8140, on CLAIM board 8025). Evidence: ch01's GATE OK board 8659. Batch E2i, item 6 of 10. Gate: ch01 GATE OK board 8659 (tree 424e040dda) Commits folded into this step (1): - 633ea4d5e4 [rush-daemon] Test validateEngineShape with two phases and two plugins Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Record a detached child before it can be orphaned Swarm integration step 225; original commit 181ec65700 (merge of swarm/r06-t321 at 59f0c8f300). Scope: task 321, record a detached child before it can be orphaned. Brings 321 (branch swarm/r06-t321). Second agent: o02 board 8396 (RESULT board 8395, on CLAIM board 7923). Evidence: ch01's GATE OK board 8659. Batch E2i, item 7 of 10. Gate: ch01 GATE OK board 8659 (tree 467af3a212) Commits folded into this step (1): - 59f0c8f300 rush-daemon-transport: record a detached child before it can be orphaned Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Measure the daemon's resident memory from VmRSS Swarm integration step 226; original commit e8ee778207 (merge of swarm/r06-t323 at b38adbe43a). Scope: task 323, the daemon's resident memory from VmRSS. Brings 323 (branch swarm/r06-t323). Second agent: r02 board 8280 (RESULT board 8278, on CLAIM board 7797). Evidence: ch01's GATE OK board 8659. Batch E2i, item 8 of 10. Gate: ch01 GATE OK board 8659 (tree ef3ff6e78e) Commits folded into this step (1): - b38adbe43a rush-daemon: measure the daemon's resident memory from VmRSS Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Test closedAfterReadingAll after abort() and sendFrameWrittenAsync on a closed connection Swarm integration step 227; original commit 6dfc4646a2 (merge of o07/t333-e2i at a3913b512e). Scope: task 333, C' on E2h's end (C = 0efecbc6f1). Brings 333 (branch o07/t333-e2i). Second agent: o04 board 8182 (RESULT board 8180, on CLAIM board 8139). Evidence: ch01's GATE OK board 8659. Batch E2i, item 9 of 10. Gate: ch01 GATE OK board 8659 (tree d253b440c5) Commits folded into this step (1): - a3913b512e Test closedAfterReadingAll after abort() and sendFrameWrittenAsync on a closed connection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test the digest pool's default idle timeout and dispose Swarm integration step 228; original commit 3bb2e042c7 (merge of swarm/r06-t336 at ef311f87b7). Scope: task 336, the digest pool's idle timeout and dispose. Brings 336 (branch swarm/r06-t336). Second agent: r03 board 8232 (RESULT board 8231, on CLAIM board 8193). Evidence: ch01's GATE OK board 8659. Batch E2i, item 10 of 10. Gate: ch01 GATE OK board 8659 (tree ea24ac746b) Commits folded into this step (1): - ef311f87b7 rush-daemon: test the digest pool's default idle timeout and dispose Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Let a request with an explicit wait timeout wait for an iteration to start to join it Swarm integration step 229; original commit f43e308754 (merge of swarm/r05-t109-nit3 at e33b4ad9ed). Scope: task 109 NIT 3. Brings 109n3 (branch swarm/r05-t109-nit3). Second agent: o04 board 8840. Evidence: ch01's GATE OK board 9622. Batch E2j, item 1 of 8. Gate: ch01 GATE OK board 9622 (tree 88bf752ab9) Commits folded into this step (1): - e33b4ad9ed [rush-daemon] Let a request with an explicit wait timeout wait for an iteration to start to join it Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test unknown names and a warm engine with no plugins Swarm integration step 230; original commit 98736251df (merge of swarm/r04-t338 at 0293d90955). Scope: task 338, 334's EVERY NIT. Brings 338 (branch swarm/r04-t338). Second agent: o04 board 8305. Evidence: ch01's GATE OK board 9622. Batch E2j, item 2 of 8. Gate: ch01 GATE OK board 9622 (tree 0366e8f0cb) Commits folded into this step (1): - 0293d90955 [rush-daemon] Test unknown names and a warm engine with no plugins Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test the idle timeout that a digester gives its pool by default Swarm integration step 231; original commit 0f46898a73 (merge of swarm/r06-t339 at 916c31257e). Scope: task 339, 336 NIT: pin the digest pool's default idle timeout. Brings 339 (branch swarm/r06-t339). Second agent: r03 board 8391. Evidence: ch01's GATE OK board 9622. Batch E2j, item 3 of 8. Gate: ch01 GATE OK board 9622 (tree f03d78b876) Commits folded into this step (1): - 916c31257e rush-daemon: test the idle timeout that a digester gives its pool by default Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test the lifetime defaults of a daemon that owns its process Swarm integration step 232; original commit d59ee79572 (merge of swarm/r06-t343 at 210b5613cb). Scope: task 343, pin the process-owning daemon's defaults. Brings 343 (branch swarm/r06-t343). Second agent: o04 board 9009. Evidence: ch01's GATE OK board 9622. Batch E2j, item 4 of 8. Gate: ch01 GATE OK board 9622 (tree 767f7d328d) Commits folded into this step (1): - 210b5613cb rush-daemon: test the lifetime defaults of a daemon that owns its process Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon-transport] Test the spawn mark and own group reads without a process Swarm integration step 233; original commit a1a015252b (merge of swarm/r06-t344 at 5cdc913318). Scope: task 344, 321 test NIT: no-process tests for the spawn mark writer. Brings 344 (branch swarm/r06-t344). Second agent: r03 board 8813. Evidence: ch01's GATE OK board 9622. Batch E2j, item 5 of 8. Gate: ch01 GATE OK board 9622 (tree 205fcb07d8) Commits folded into this step (1): - 5cdc913318 rush-daemon-transport: test the spawn mark and own group reads without a process Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Test two cases of deferred build cache writes Swarm integration step 234; original commit 17a943c265 (merge of swarm/r07-t341 at 6bd98980ae). Scope: task 341, 113 follow-up: tests that kill two survivors. Brings 341 (branch swarm/r07-t341). Second agent: t06 board 9032. Evidence: ch01's GATE OK board 9622. Batch E2j, item 6 of 8. Gate: ch01 GATE OK board 9622 (tree fbdd18d92e) Commits folded into this step (1): - 6bd98980ae [rush-lib] Test two cases of deferred build cache writes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Document the silent stop by a served reload, rebuild or retest Swarm integration step 235; original commit bf1ac546df (merge of swarm/r04-t340 at 665d8ec1b5). Scope: task 340, 108 README NIT. Brings 340 (branch swarm/r04-t340). Second agent: t07 board 8441. Evidence: ch01's GATE OK board 9622. Batch E2j, item 7 of 8. Gate: ch01 GATE OK board 9622 (tree 1c0a57129d) Commits folded into this step (1): - 665d8ec1b5 [rush-cli-client] Document the silent stop by a served reload, rebuild or retest Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Say which operations rushd stops for a served command Swarm integration step 236; original commit ca8c052981 (merge of swarm/r04-t345 at 857fc03605). Scope: task 345, 340 follow-up: say which operations a served request stops. Brings 345 (branch swarm/r04-t345, 3 commits). Second agent: t07 board 9019 (m01 METRICS board 9124). GATE NIT: mutant M345-ANY-POSITION survives; the test-only follow-up is task 368 (D64 board 9574). Evidence: ch01's GATE OK board 9622. Batch E2j, item 8 of 8. Gate: ch01 GATE OK board 9622 (tree ed25ba3c6c) Commits folded into this step (3): - f9ccf77215 [rush-daemon-protocol] Add a stopping flag to IDaemonContinuingOperations - 406b0d8325 [rush-daemon] Name the continuing operations that a queued request makes rushd stop - 857fc03605 [rush-cli-client] Say which operations rushd stops for a served command Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rig-package] Name the package in the missing "-rig" suffix error Swarm integration step 237; original commit 3e71185392 (merge of swarm/o08-d551-t2 at 66bf274454). Scope: task 350, D55.1 T2: rig-package's missing -rig suffix error names the wrong field. Brings 350 (branch swarm/o08-d551-t2). Second agent: o03 board 9087 (CLAIM board 8944). Evidence: ch01's GATE OK board 9991. Batch E2k part 1 (K1), item 1 of 5. Gate: ch01 GATE OK board 9991 (tree da36e22e70) Commits folded into this step (1): - 66bf274454 rig-package: name the package in the missing "-rig" suffix error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Quote custom parameter values for the shell that runs a project's script Swarm integration step 238; original commit dc83bf8af5 (merge of swarm/r03-d551-t1 at e89a36bdea). Scope: task 349, D55.1 T1: quote custom parameter values in shell commands. Brings 349 (branch swarm/r03-d551-t1). Second agent: o08 board 9188. Evidence: ch01's GATE OK board 9991. Batch E2k part 1 (K1), item 2 of 5. Gate: ch01 GATE OK board 9991 (tree 73b7b3b630) Commits folded into this step (1): - e89a36bdea [rush-lib] Quote custom parameter values for the shell that runs a project's script Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-lib] Read a warm worker's input folders before its command starts Swarm integration step 239; original commit 83cc7cfc3d (merge of swarm/o07-d551-t5 at 5de59478c8). Scope: task 353, D55.1 T5: a warm TypeScript watch worker sees edits after a folder is recreated. Brings 353 (branch swarm/o07-d551-t5). Second agent: t03 board 9224. Evidence: ch01's GATE OK board 9991. Batch E2k part 1 (K1), item 3 of 5. Gate: ch01 GATE OK board 9991 (tree def670f873) Commits folded into this step (1): - 5de59478c8 [rush-lib] Read a warm worker's input folders before its command starts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Name the operations that rushd stops to a request that already waits first Swarm integration step 240; original commit 5f436fbe16 (merge of swarm/r04-t361 at 5156e5a0c6). Scope: task 361, 345 follow-up: a request queued before a failed build becomes preemptible. Brings 361 (branch swarm/r04-t361). Second agent: t07 board 9533 (RESULT board 9532). Evidence: ch01's GATE OK board 9991. Batch E2k part 1 (K1), item 4 of 5. Gate: ch01 GATE OK board 9991 (tree 964d518dd4) Commits folded into this step (1): - 5156e5a0c6 [rush-daemon] Name the operations that rushd stops to a request that already waits first Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-bridge-cache-plugin] Act only on the operations that the command selects (and 1 more) Swarm integration step 241; original commit 0a7af6a3df (merge of swarm/r03-row23 at 4a746d0348). Scope: task 363, row 23: rush-bridge-cache-plugin acts only on the selected operations. Brings 363 (branch swarm/r03-row23, 2 commits). Second agent: ch05 board 9400. Evidence: ch01's GATE OK board 9991. Batch E2k part 1 (K1), item 5 of 5. Gate: ch01 GATE OK board 9991 (tree 228f843dab) Commits folded into this step (2): - 92b277d814 [rush-bridge-cache-plugin] Act only on the operations that the command selects - 4a746d0348 [rush-bridge-cache-plugin] Test a selected operation whose record is disabled Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Prepare the next engine in the background Swarm integration step 242; original commit b20e9983fa (merge of swarm/r01-t117-p2 at ab4690c4b3). Refs: task 117. Scope: task 117 Part 2: prepare the next engine in the background. Brings R (branch swarm/r01-t117-p2). Second agent: r02 board 9649 (RESULT board 9647). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 1 of 9. Gate: ch01 GATE OK board 10189 (tree 3c32e43959) Commits folded into this step (1): - ab4690c4b3 rush-daemon: Prepare the next engine in the background (task 117) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Count the capture's calls in BackgroundPrepare's tests Swarm integration step 243; original commit fb7eaa0bbf (merge of swarm/r01-t117-p2-count at 49b2a1b5c6). Refs: board 9212. Scope: task 117 Part 2 follow-up: count the capture's calls in BackgroundPrepare's tests. Brings F (branch swarm/r01-t117-p2-count). Second agent: r02 board 9865 (RESULT board 9863). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 2 of 9. Gate: ch01 GATE OK board 10189 (tree c3f57b7c24) Commits folded into this step (1): - 49b2a1b5c6 rush-daemon: Count the capture's calls in BackgroundPrepare's tests (board 9212) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Test that a preparation failure before the reload stops preparing Swarm integration step 244; original commit 66229fc83a (merge of swarm/r01-t359 at 99625a4eb6). Refs: task 359. Scope: task 359: a preparation failure before the reload stops preparing. Brings T (branch swarm/r01-t359). Second agent: ch05 board 9916 (RESULT board 9915). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 3 of 9. Gate: ch01 GATE OK board 10189 (tree 65d1ae3729) Commits folded into this step (1): - 99625a4eb6 rush-daemon: Test that a preparation failure before the reload stops preparing (task 359) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Drop internal references from test names and comments Swarm integration step 245; original commit 1faa9f9d1f (merge of swarm/r02-d551-pr-hygiene at a82c9c190f). Scope: task 355, PR hygiene: drop internal references from test names and comments. Brings 355 (branch swarm/r02-d551-pr-hygiene). Second agent: ch05 board 9628 (RESULT board 9627). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 4 of 9. Gate: ch01 GATE OK board 10189 (tree cfc98bc3b8) Commits folded into this step (1): - a82c9c190f Drop internal references from test names and comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep rush-daemon test socket paths short however long TMPDIR is Swarm integration step 246; original commit 5ab3a13dda (merge of swarm/r02-d551-t4 at d1b14ef3c3). Scope: task 352, D55.1 T4: rush-daemon test socket paths stay short however long TMPDIR is. Brings 352 (branch swarm/r02-d551-t4). Second agent: r01 board 9940 (RESULT board 9937). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 5 of 9. Gate: ch01 GATE OK board 10189 (tree 77ba7e3ebe) Commits folded into this step (1): - d1b14ef3c3 Keep rush-daemon test socket paths short however long TMPDIR is Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-cli-client] Name `rush-client daemon status` when rushd has not responded Swarm integration step 247; original commit b41e167dbc (merge of swarm/r04-t364 at 455b9b1940). Scope: task 364: the silence line names `rush-client daemon status`. Brings 364 (branch swarm/r04-t364). Second agent: t07 board 9930 (RESULT board 9929). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 6 of 9. Gate: ch01 GATE OK board 10189 (tree a9bde896df) Commits folded into this step (1): - 455b9b1940 [rush-cli-client] Name `rush-client daemon status` when rushd has not responded Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Hash the paths that "git hash-object" can't read Swarm integration step 248; original commit 86204d0550 (merge of swarm/r06-d551-t3 at 298b23b0d2). Scope: task 351, D55.1 T3: package-deps-hash hashes the paths that git hash-object can't read. Brings 351 (branch swarm/r06-d551-t3). Second agent: t04 board 10037. Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 7 of 9. Gate: ch01 GATE OK board 10189 (tree 772d755384) Commits folded into this step (1): - 298b23b0d2 package-deps-hash: hash the paths that "git hash-object" can't read Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [package-deps-hash] Resolve a relative RepoStateCache temporary folder path against the working directory (and 1 more) Swarm integration step 249; original commit 5aecba33b6 (merge of swarm/r06-t366 at 03a65659c2). Scope: task 366, 351 follow-ups: sign an input that is a link to no regular file by the link itself. Brings 366 (branch swarm/r06-t366, 2 commits). Second agent: t04 board 10037. Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 8 of 9. Gate: ch01 GATE OK board 10189 (tree 506a5b0862) Commits folded into this step (2): - 720874a782 [package-deps-hash] Resolve a relative RepoStateCache temporary folder path against the working directory - 03a65659c2 [rush-lib] Sign an input that is a symbolic link not leading to a regular file by the link itself Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c * [rush-daemon] Keep the environment out of failed test assertions (and 1 more) Swarm integration step 250; original commit 051bfee3e4 (merge of swarm/o01-t365 at fb77adfde9). Scope: task 365, C1 and C2: rush-daemon tests print no environment in failed assertions, and delete secret-shaped variables first. Brings 365 (branch swarm/o01-t365, 2 commits). Second agent: r01 board 9962 (RESULT board 9901). Evidence: ch01's GATE OK board 10189. Batch E2k part 2 (K2), item 9 of 9. Gate: ch01 GATE OK board 10189 (tree 090484c717) Commits folded into this step (2): - df1f14139a rush-daemon: keep the environment out of failed test assertions - fb77adfde9 rush-daemon: delete secret-shaped environment variables before each test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c --------- Co-authored-by: Sean Larkin <3408176+TheLarkInn@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6d3da49f-95f3-4291-9026-0b0f8e38675c --- .../src/cli/HeftCommandLineParser.test.ts | 140 ++ apps/heft/src/cli/HeftCommandLineParser.ts | 9 +- apps/heft/src/utilities/CliUtilities.ts | 12 + .../src/utilities/WatchFileSystemAdapter.ts | 171 +- .../src/utilities/test/CliUtilities.test.ts | 26 + .../test/WatchFileSystemAdapter.test.ts | 588 +++++ apps/rush-cli-client/README.md | 553 ++++- apps/rush-cli-client/package.json | 3 + apps/rush-cli-client/src/AgentNotices.ts | 51 + .../src/AgentOperationTracker.ts | 279 +++ .../src/AgentProgressRenderer.ts | 843 +++++++ .../src/ClientAdmissionControls.ts | 39 +- .../src/GraphRequestContext.ts | 2 + .../src/OperationOutputExcerpt.ts | 272 +++ .../rush-cli-client/src/clientCancellation.ts | 89 + apps/rush-cli-client/src/clientOutput.ts | 157 ++ .../src/continuingOperations.ts | 30 + apps/rush-cli-client/src/daemonCommands.ts | 416 +++- .../src/daemonConnectionOptions.ts | 23 +- apps/rush-cli-client/src/daemonGraph.ts | 7 +- .../src/daemonReclaimNotice.ts | 57 + .../src/daemonRestartNotice.ts | 377 +++ apps/rush-cli-client/src/daemonSilence.ts | 77 + apps/rush-cli-client/src/earlyRouting.ts | 89 + apps/rush-cli-client/src/inProcessFallback.ts | 16 + apps/rush-cli-client/src/inProcessLockWait.ts | 49 + apps/rush-cli-client/src/launchClient.ts | 318 ++- apps/rush-cli-client/src/lazyRushModules.ts | 5 + .../src/nativeLockWaitNotice.ts | 160 ++ apps/rush-cli-client/src/outputSelection.ts | 150 ++ apps/rush-cli-client/src/resultDiagnostics.ts | 45 + apps/rush-cli-client/src/routing.ts | 120 +- apps/rush-cli-client/src/start.ts | 48 +- apps/rush-cli-client/src/startRushX.ts | 8 +- apps/rush-cli-client/src/terminalColumns.ts | 12 + .../src/test/AgentNotices.test.ts | 97 + .../src/test/AgentProgressRenderer.test.ts | 1881 +++++++++++++++ .../src/test/ClientAdmissionControls.test.ts | 54 +- .../src/test/GraphGenerationWire.test.ts | 5 +- .../src/test/NativeBuildTestFixture.test.ts | 30 + .../src/test/NativeBuildTestFixture.ts | 9 +- .../src/test/OperationOutputExcerpt.test.ts | 303 +++ .../src/test/PersistentIpcTestFixture.ts | 3 + .../src/test/RushXDaemon.test.ts | 8 + .../src/test/RushXDaemonAlias.test.ts | 33 +- .../src/test/RushXDaemonBoundaries.test.ts | 71 +- .../src/test/RushXDaemonTestFixture.ts | 45 +- .../src/test/RushXTerminal.test.ts | 8 +- .../src/test/ShutdownExitDaemonTestProcess.ts | 58 + .../src/test/TestProcessEnvironment.ts | 18 + .../src/test/cancellationNotice.test.ts | 548 +++++ .../src/test/clientOutput.test.ts | 159 ++ .../test/daemonConnectionSelection.test.ts | 46 + .../src/test/daemonGraph.test.ts | 3 +- .../src/test/daemonLiveOwner.test.ts | 282 +++ .../src/test/daemonLogs.test.ts | 11 +- .../src/test/daemonReclaimNotice.test.ts | 72 + .../src/test/daemonRestartNotice.test.ts | 837 +++++++ .../src/test/daemonSilence.test.ts | 61 + .../src/test/daemonSilenceOutput.test.ts | 146 ++ .../src/test/earlyRouting.test.ts | 67 + .../src/test/inProcessFallback.test.ts | 55 + .../src/test/inProcessLockWait.test.ts | 231 ++ .../src/test/launchClient.test.ts | 686 +++++- .../src/test/launchClientResultStderr.test.ts | 171 ++ .../src/test/nativeBuild.test.ts | 323 ++- .../src/test/nativeLockWaitNotice.test.ts | 280 +++ .../src/test/nativeMutation.test.ts | 4 +- .../src/test/omitWarmSetStatus.test.ts | 104 + .../src/test/outputSelection.test.ts | 99 + .../test/persistentIpcCancellation.test.ts | 11 +- .../src/test/pipedInput.test.ts | 18 +- .../src/test/resultDiagnostics.test.ts | 89 + .../src/test/returnEarlyOnFailure.test.ts | 100 + apps/rush-cli-client/src/test/routing.test.ts | 190 +- .../src/test/rushxFallbackLines.test.ts | 137 ++ .../src/test/sentBackFallback.test.ts | 73 + .../src/test/startupPendingFallback.test.ts | 177 ++ .../src/test/terminalColumns.test.ts | 18 + .../src/test/versionSelectionFallback.test.ts | 5 +- ...able-engine-creation_2026-09-29-19-35.json | 11 + ...e-credential-refresh_2026-09-28-21-50.json | 11 + ...-endpoint-credential_2026-09-29-00-29.json | 11 + ...rage-endpoint-schema_2026-09-28-22-28.json | 11 + ...round-prepare-option_2026-09-29-22-40.json | 11 + ...eration-announcement_2026-09-29-11-45.json | 11 + .../before-log-request_2026-09-29-14-30.json | 11 + ...-selected-operations_2026-09-28-18-45.json | 11 + ...cache-output-receipt_2026-09-29-18-00.json | 11 + ...e-receipt-more-tests_2026-09-29-21-12.json | 11 + ...-cache-receipt-tests_2026-09-29-19-08.json | 11 + ...staged-restore-tests_2026-09-29-21-58.json | 11 + ...cache-staged-restore_2026-09-29-18-58.json | 11 + ...ure-input-files-late_2026-09-29-12-56.json | 10 + ...de-code-keeps-daemon_2026-09-29-03-18.json | 10 + ...closed-output-reader_2026-09-29-11-50.json | 11 + ...and-agnostic-plugins_2026-09-28-22-40.json | 11 + ...n-compatible-plugins_2026-09-28-13-00.json | 10 + ...on-request-telemetry_2026-09-28-13-25.json | 10 + ...ing-names-parameters_2026-09-28-23-02.json | 11 + ...sharing-names-setter_2026-09-29-15-01.json | 11 + ...telemetry-flush-wait_2026-09-28-19-20.json | 10 + ...d-cache-writes-tests_2026-09-30-02-38.json | 11 + ...eferred-cache-writes_2026-09-29-18-45.json | 11 + ...closure-plugin-tests_2026-09-29-12-37.json | 11 + ...build-cache-poisoning-race_2026-09-24.json | 9 + .../fix-warmset-cap_2026-09-24-01-00.json | 10 + .../rush/flag-reload_2026-09-24.json | 11 + ...-snapshot-hash-check_2026-09-29-15-16.json | 11 + ...al-guard-input-files_2026-09-29-08-46.json | 11 + ...ntal-inputs-snapshot_2026-09-29-04-15.json | 11 + ...script-outside-watch_2026-09-28-14-05.json | 11 + ...apshot-during-checks_2026-09-29-05-45.json | 11 + ...ed-input-files-start_2026-09-29-21-59.json | 11 + ...-running-batch-begin_2026-09-29-19-15.json | 11 + ...running-batch-option_2026-09-29-17-55.json | 11 + ...ng-dispatched-inputs_2026-09-29-21-15.json | 11 + ...in-running-iteration_2026-09-29-14-20.json | 11 + ...ache-disabled-reason_2026-09-28-23-08.json | 11 + ...acy-skip-input-files_2026-09-29-10-18.json | 10 + .../m03-t179-cpu-info_2026-09-29-07-52.json | 10 + ...82-flag-temp-folders_2026-09-29-08-17.json | 10 + ...3-t215-critical-path_2026-09-29-06-55.json | 10 + ...tom-parameter-values_2026-09-30-05-35.json | 11 + ...6-ipc-engine-sharing_2026-09-29-08-55.json | 11 + ...legacy-skip-disabled_2026-09-29-03-58.json | 11 + ...cy-skip-invalidation_2026-09-29-03-30.json | 11 + ...03-parent-short-name_2026-09-29-02-40.json | 11 + .../r03-runtime-folder_2026-09-28-17-15.json | 11 + ...ush-lib-path-handoff_2026-09-28-13-10.json | 11 + ...3-socket-path-length_2026-09-28-19-19.json | 11 + ...package-manager-lock_2026-09-29-07-30.json | 11 + ...ed-disableBuildCache_2026-09-29-06-24.json | 11 + ...y-lock-wait-prettier_2026-09-29-17-57.json | 11 + ...y-lock-wait-test-cap_2026-09-29-15-40.json | 11 + ...request-results-hook_2026-09-29-01-22.json | 10 + ...ugin-skipped-results_2026-09-28-18-39.json | 11 + ...use-unchanged-inputs_2026-09-28-18-30.json | 10 + ...unchanged-repo-state_2026-09-28-21-15.json | 11 + ...changed-state-hashes_2026-09-29-07-04.json | 11 + ...-sdk-export-snapshot_2026-09-29-13-45.json | 11 + .../rushd-cancel-hard-abort_2026-09-23.json | 11 + .../rush/rushd-env-identity_2026-09-28.json | 11 + .../rushd-incremental-builds_2026-09-28.json | 11 + ...incremental-plugin-runners_2026-09-28.json | 11 + .../rushd-iteration-hook-env_2026-09-28.json | 11 + .../rush/rushd-operation-env_2026-09-28.json | 11 + ...rushd-operation-request-id_2026-09-28.json | 11 + ...ushd-request-operation-env_2026-09-28.json | 11 + ...napshot-window-input-guard_2026-09-28.json | 11 + ...rushd-summary-banner_2026-09-24-01-30.json | 11 + ...nverifiable-changed-inputs_2026-09-28.json | 11 + ...rs-disabled-operation-test_2026-09-29.json | 11 + .../rush/rushd-warm-workers_2026-09-28.json | 11 + ...stom-phased-commands_2026-09-28-17-52.json | 11 + ...ased-command-engines_2026-09-28-20-55.json | 11 + ...-cache-hit-error-log_2026-09-29-00-20.json | 11 + ...in-process-lock-wait_2026-09-29-13-55.json | 11 + ...04-flush-before-exit_2026-09-29-05-08.json | 11 + ...sing-shrinkwrap-deps_2026-09-28-16-20.json | 11 + ...-configuration-error_2026-09-28-16-20.json | 11 + ...package-manager-lock_2026-09-28-17-58.json | 11 + ...6-watch-run-requests_2026-09-29-09-15.json | 11 + ...its-during-iteration_2026-09-29-10-50.json | 11 + ...-watch-provider-test_2026-09-29-13-00.json | 11 + ...r02-t298-warned-base_2026-09-29-16-26.json | 11 + ...rm-r04-t78-erroropts_2026-09-29-03-57.json | 11 + ...m-r04-t78-nit8-usage_2026-09-29-21-38.json | 11 + ...-r04-t78-usage-error_2026-09-29-00-14.json | 11 + ...97-served-script-end_2026-09-29-05-10.json | 11 + ...gine-json-load-cache_2026-09-28-23-50.json | 10 + ...ested-config-project_2026-09-28-12-25.json | 9 + ...restart-wait-reasons_2026-09-29-02-05.json | 11 + ...5-warm-set-retention_2026-09-28-12-26.json | 9 + ...r06-link-input-stats_2026-09-30-08-20.json | 11 + ...248-operation-marker_2026-09-29-15-30.json | 11 + ...etained-result-trust_2026-09-28-14-10.json | 10 + ...rig-profile-realpath_2026-09-28-13-00.json | 10 + ...ify-retained-results_2026-09-28-15-25.json | 10 + ...try-tag-keeps-daemon_2026-09-29-01-38.json | 10 + ...test-name-references_2026-09-30-07-55.json | 11 + ...-blocks-cache-writes_2026-09-29-07-40.json | 10 + ...erified-plugin-skips_2026-09-29-17-51.json | 11 + ...er-first-run-folders_2026-09-30-05-40.json | 11 + ...e-redact-parse-error_2026-09-28-22-44.json | 11 + ...al-cache-schema-once_2026-09-28-21-50.json | 11 + .../lint-in-watch-mode_2026-09-28.json | 10 + ...int-cache-suppressed_2026-09-29-09-28.json | 10 + ...k-plugin-watch-flush_2026-09-29-11-55.json | 11 + .../realpath-missing-paths_2026-09-28.json | 10 + .../watch-missing-root-files_2026-09-29.json | 10 + ...4-plugin-watch-flush_2026-09-29-11-55.json | 11 + ...ch-test-node-version_2026-09-29-15-05.json | 11 + ...assets-in-watch-mode_2026-09-29-10-25.json | 10 + ...5-plugin-watch-flush_2026-09-29-09-18.json | 11 + ...ose-previous-watcher_2026-09-29-08-00.json | 11 + ...p-changes-a-run-read_2026-09-29-14-45.json | 11 + ...me-and-unread-folder_2026-09-29-16-37.json | 11 + ...heft-error-exit-code_2026-09-29-07-05.json | 11 + .../lockfile-birthtime-tie_2026-09-28.json | 10 + .../lockfile-exited-process_2026-09-29.json | 10 + .../lockfile-own-start-time_2026-09-28.json | 10 + .../lockfile-proc-start-time_2026-09-28.json | 10 + ...lockfile-start-time-locale_2026-09-28.json | 10 + .../lockfile-time-zone-tests_2026-09-29.json | 10 + ...tion-graph-exact-rss_2026-09-29-23-35.json | 11 + ...t-config-fingerprint_2026-09-29-04-55.json | 11 + ...dex-carry-over-tests_2026-09-29-05-10.json | 11 + .../git-status-first_2026-09-29-05-45.json | 11 + ...dex-caches-on-recopy_2026-09-29-02-37.json | 11 + ...he-hidden-attributes_2026-09-29-12-05.json | 11 + ...ate-cache-more-tests_2026-09-29-03-15.json | 11 + ...ache-sparse-checkout_2026-09-29-14-52.json | 11 + .../repo-state-cache_2026-09-28-21-15.json | 11 + ...01-git-error-command_2026-09-28-15-02.json | 11 + ...ash-object-rejection_2026-09-28-14-47.json | 11 + ...relative-temp-folder_2026-09-30-08-20.json | 11 + ...r06-unhashable-paths_2026-09-30-06-07.json | 11 + .../swarm-o08-d551-t2_2026-09-30-05-38.json | 11 + ...mission-failure-code_2026-09-28-15-08.json | 11 + ...gent-empty-selection_2026-09-28-14-45.json | 11 + .../agent-error-detail_2026-09-28-13-30.json | 11 + ...tput-engine-warnings_2026-09-28-13-52.json | 10 + .../agent-output-scale_2026-09-28-13-00.json | 11 + .../agent-pipe-status_2026-09-28-14-20.json | 11 + .../agent-reporter_2026-09-24-02-35.json | 11 + .../agent-startup-wait-owner_2026-09-29.json | 11 + ...round-prepare-option_2026-09-29-22-40.json | 11 + ...l-before-engine-init_2026-09-28-14-45.json | 11 + .../daemon-recovery_2026-09-23.json | 10 + .../daemon-shutdown-log_2026-09-29-01-40.json | 10 + ...utdown-not-cancelled_2026-09-28-13-00.json | 11 + ...eferred-cache-writes_2026-09-29-18-45.json | 11 + ...utation-keeps-daemon_2026-09-29-19-25.json | 11 + ...ushd-error-surfacing_2026-09-24-01-50.json | 11 + .../fix-warmset-cap_2026-09-24-01-00.json | 10 + ...ispatch-wait-timeout_2026-09-30-02-47.json | 11 + ...running-batch-option_2026-09-29-17-55.json | 11 + ...-fixture-maintenance_2026-09-29-08-44.json | 10 + .../ping-omit-warm-set_2026-09-29-09-10.json | 11 + ...ueued-request-resend_2026-09-29-17-10.json | 11 + ...nt6-rushx-test-seams_2026-09-29-08-55.json | 11 + ...reservation-relaunch_2026-09-28-23-50.json | 11 + ...explain-daemon-crash_2026-09-28-15-52.json | 11 + ...installation-changed_2026-09-28-18-45.json | 11 + ...llation-restart-wait_2026-09-28-20-30.json | 11 + .../r03-runtime-folder_2026-09-28-17-15.json | 11 + ...ush-lib-path-handoff_2026-09-28-13-10.json | 11 + ...3-socket-path-length_2026-09-28-19-19.json | 11 + ...-startup-reservation_2026-09-28-14-25.json | 11 + ...resolves-reservation_2026-09-28-15-31.json | 11 + ...108o-continuing-wait_2026-09-29-19-45.json | 11 + ...04-t108o-early-names_2026-09-29-18-45.json | 11 + ...08o-stopped-fallback_2026-09-29-20-20.json | 11 + ...-failure-and-escapes_2026-09-29-03-00.json | 11 + .../r06-t222-nit1_2026-09-29-09-55.json | 11 + ...-restart-notice-once_2026-09-29-07-20.json | 11 + ...nment-restart-reason_2026-09-29-01-35.json | 11 + ...y-lock-wait-prettier_2026-09-29-17-57.json | 11 + ...ait-with-lock-holder_2026-09-29-15-56.json | 11 + .../rushd-cancel-hard-abort_2026-09-23.json | 11 + .../rushd-incremental-builds_2026-09-28.json | 11 + ...ushd-request-operation-env_2026-09-28.json | 11 + .../rushd-warm-workers_2026-09-28.json | 11 + ...est-readiness-notice_2026-09-29-11-17.json | 10 + ...cript-wait-followers_2026-09-29-21-50.json | 11 + ...tup-pending-fallback_2026-09-28-16-58.json | 11 + .../status-no-daemon_2026-09-28-23-45.json | 11 + .../stop-while-starting_2026-09-28-21-51.json | 11 + ...111-native-lock-wait_2026-09-29-11-57.json | 11 + ...o04-t192-reclaim-log_2026-09-29-03-52.json | 11 + ...199-force-reset-reap_2026-09-29-04-52.json | 11 + ...penable-launcher-log_2026-09-29-08-28.json | 11 + ...in-process-lock-wait_2026-09-29-13-55.json | 11 + ...5-t226-closed-output_2026-09-29-08-40.json | 11 + ...aemon-silence-stderr_2026-09-29-12-30.json | 11 + ...-t108-early-failures_2026-09-28-18-14.json | 10 + ...1-lock-wait-resubmit_2026-09-29-22-40.json | 11 + .../swarm-r04-t108-nits_2026-09-28-21-10.json | 11 + ...04-t108-return-early_2026-09-28-20-25.json | 11 + ...o-resubmitted-status_2026-09-29-21-10.json | 11 + ...4-t132-cancel-notice_2026-09-28-22-10.json | 11 + .../swarm-r04-t132-nit1_2026-09-29-03-10.json | 11 + ...t142-operation-error_2026-09-28-22-50.json | 11 + ...4-t165-late-warnings_2026-09-29-02-35.json | 11 + ...4-t170-fallback-line_2026-09-29-02-10.json | 11 + .../swarm-r04-t170-nit3_2026-09-29-04-45.json | 11 + ...04-t297-not-run-line_2026-09-29-16-30.json | 11 + ...t297-owner-wait-hint_2026-09-29-16-20.json | 11 + ...m-r04-t297-reap-wait_2026-09-29-16-50.json | 11 + ...7-stop-stopped-owner_2026-09-29-16-10.json | 11 + ...-stopped-lock-holder_2026-09-29-16-45.json | 11 + ...306-lock-holder-exit_2026-09-29-17-45.json | 11 + ...-wait-resubmit-tests_2026-09-29-23-48.json | 11 + ...0-readme-served-stop_2026-09-30-02-45.json | 11 + ...-stopping-continuing_2026-09-30-04-40.json | 11 + ...silence-names-status_2026-09-30-08-15.json | 11 + .../swarm-r04-t60-nit2_2026-09-29-07-56.json | 11 + ...04-t60-route-reasons_2026-09-29-03-55.json | 11 + ...4-t69-daemon-silence_2026-09-29-07-20.json | 11 + ...-r04-t69b-live-owner_2026-09-29-10-38.json | 11 + ...4-t69b-stopped-owner_2026-09-29-11-40.json | 11 + ...m-r04-t78-nit8-usage_2026-09-29-21-38.json | 11 + ...t83-admission-reason_2026-09-28-18-55.json | 10 + ...83-startup-wait-line_2026-09-28-20-10.json | 10 + ...97-served-script-end_2026-09-29-05-10.json | 11 + ...ssion-failure-reason_2026-09-28-17-39.json | 10 + ...mission-wait-timeout_2026-09-28-13-45.json | 10 + ...paration-wait-budget_2026-09-28-18-20.json | 10 + .../swarm-r05-t166-nits_2026-09-29-05-25.json | 11 + ...restart-wait-reasons_2026-09-29-02-05.json | 11 + ...-installation-change_2026-09-29-04-25.json | 11 + ...-incremental-command_2026-09-28-23-00.json | 11 + ...54-inprocess-reclaim_2026-09-28-23-56.json | 11 + ...-t177-reclaim-notice_2026-09-29-02-10.json | 11 + ...-reused-owner-groups_2026-09-29-08-40.json | 11 + ...248-operation-marker_2026-09-29-15-30.json | 11 + ...-groups-left-running_2026-09-29-17-50.json | 11 + ...test-name-references_2026-09-30-07-55.json | 11 + .../zero-size-pty_2026-09-28-13-00.json | 11 + .../daemon-recovery_2026-09-23.json | 10 + ...daemon-startup-retry-tests_2026-09-29.json | 11 + ...-restart-arbitration_2026-09-24-01-45.json | 10 + ...ushd-error-surfacing_2026-09-24-01-50.json | 11 + ...s-ping-omit-warm-set_2026-09-29-10-20.json | 11 + .../ping-omit-warm-set_2026-09-29-09-10.json | 11 + ...ueued-request-resend_2026-09-29-17-10.json | 11 + ...reservation-relaunch_2026-09-28-23-50.json | 11 + ...explain-daemon-crash_2026-09-28-15-52.json | 11 + ...installation-changed_2026-09-28-18-45.json | 11 + ...llation-restart-wait_2026-09-28-20-30.json | 11 + ...akeover-recheck-test_2026-09-29-02-55.json | 11 + .../r03-runtime-folder_2026-09-28-17-15.json | 11 + ...3-socket-path-length_2026-09-28-19-19.json | 11 + ...-startup-reservation_2026-09-28-14-25.json | 11 + ...resolves-reservation_2026-09-28-15-31.json | 11 + ...108o-continuing-wait_2026-09-29-19-45.json | 11 + ...84-nits-timeout-test_2026-09-29-10-10.json | 11 + ...-failure-and-escapes_2026-09-29-03-00.json | 11 + ...ad-all-before-resend_2026-09-29-20-21.json | 11 + ...t-socket-bigint-stat_2026-09-29-14-55.json | 11 + ...rain-default-timeout_2026-09-28-15-08.json | 11 + ...rt-planned-successor_2026-09-28-19-50.json | 11 + ...cript-wait-followers_2026-09-29-21-50.json | 11 + .../startup-burst-test_2026-09-28.json | 10 + ...tup-helper-exit-test_2026-09-29-18-10.json | 10 + .../startup-helper-exit_2026-09-29.json | 10 + ...rtup-helper-readiness-poll_2026-09-29.json | 10 + ...tup-pending-fallback_2026-09-28-16-58.json | 11 + ...tartup-reservation-release_2026-09-29.json | 10 + .../stop-while-starting_2026-09-28-21-51.json | 16 + ...111-native-lock-wait_2026-09-29-11-57.json | 11 + ...o04-t192-reclaim-log_2026-09-29-03-52.json | 11 + ...199-force-reset-reap_2026-09-29-04-52.json | 11 + ...-t213-log-line-start_2026-09-29-05-41.json | 11 + ...penable-launcher-log_2026-09-29-08-28.json | 11 + ...4-t132-cancel-notice_2026-09-28-22-10.json | 11 + ...04-t297-not-run-line_2026-09-29-16-30.json | 11 + ...t297-owner-wait-hint_2026-09-29-16-20.json | 11 + ...m-r04-t297-reap-wait_2026-09-29-16-50.json | 11 + ...7-stop-stopped-owner_2026-09-29-16-10.json | 11 + ...-stopped-lock-holder_2026-09-29-16-45.json | 11 + ...4-t69-daemon-silence_2026-09-29-07-20.json | 11 + .../swarm-r04-t69-nit1_2026-09-29-09-10.json | 11 + ...-r04-t69b-live-owner_2026-09-29-10-38.json | 11 + ...4-t69b-stopped-owner_2026-09-29-11-40.json | 11 + ...restart-wait-reasons_2026-09-29-02-05.json | 11 + ...-installation-change_2026-09-29-04-25.json | 11 + ...54-inprocess-reclaim_2026-09-28-23-56.json | 11 + ...-reclaim-after-crash_2026-09-28-23-50.json | 11 + ...-t177-reclaim-notice_2026-09-29-02-10.json | 11 + ...t203-stranded-groups_2026-09-29-05-40.json | 11 + ...-reused-owner-groups_2026-09-29-08-40.json | 11 + ...nherited-descriptors_2026-09-29-16-50.json | 11 + ...-groups-left-running_2026-09-29-17-50.json | 11 + ...06-t300-reclaim-line_2026-09-29-20-05.json | 11 + ...tput-engine-warnings_2026-09-28-13-52.json | 10 + ...gent-output-log-path_2026-09-28-13-00.json | 11 + ...-restart-arbitration_2026-09-24-18-10.json | 10 + ...ushd-error-surfacing_2026-09-24-01-50.json | 11 + .../ping-omit-warm-set_2026-09-29-09-10.json | 11 + ...ueued-request-resend_2026-09-29-17-10.json | 11 + ...installation-changed_2026-09-28-18-45.json | 11 + ...llation-restart-wait_2026-09-28-20-30.json | 11 + .../r03-runtime-folder_2026-09-28-17-15.json | 11 + ...ontinuing-operations_2026-09-29-18-50.json | 11 + ...06-t184-nits-escapes_2026-09-29-10-10.json | 11 + ...-failure-and-escapes_2026-09-29-03-00.json | 11 + ...nment-restart-reason_2026-09-29-01-15.json | 11 + ...-lock-wait-followers_2026-09-29-19-00.json | 11 + ...rain-default-timeout_2026-09-28-15-08.json | 11 + ...rt-planned-successor_2026-09-28-19-50.json | 11 + ...cript-wait-followers_2026-09-29-21-50.json | 11 + ...111-native-lock-wait_2026-09-29-11-57.json | 11 + ...04-t108-return-early_2026-09-28-20-25.json | 11 + ...-stopping-continuing_2026-09-30-04-40.json | 11 + ...4-t69-daemon-silence_2026-09-29-07-20.json | 11 + ...m-r04-t78-nit8-usage_2026-09-29-21-38.json | 11 + ...restart-wait-reasons_2026-09-29-02-05.json | 11 + ...-installation-change_2026-09-29-04-25.json | 11 + ...-incremental-command_2026-09-28-23-00.json | 11 + .../m03-t209-drain_2026-09-29-06-00.json | 11 + ...1-bigint-socket-stat_2026-09-29-07-40.json | 11 + ...-runtime-folder-file_2026-09-29-04-57.json | 11 + ...01-shutdown-deadline_2026-09-28-21-50.json | 11 + .../r01-socket-check_2026-09-29-02-10.json | 11 + .../r03-runtime-folder_2026-09-28-17-15.json | 11 + ...3-socket-path-length_2026-09-28-19-19.json | 11 + ...listener-reap-report_2026-09-29-10-36.json | 11 + .../r06-t262-test-keys_2026-09-29-11-45.json | 11 + ...awn-mark-reads-tests_2026-09-30-03-50.json | 11 + ...-before-resend-tests_2026-09-30-01-03.json | 11 + ...ad-all-before-resend_2026-09-29-20-21.json | 11 + .../rushd-orphan-groups_2026-09-28.json | 11 + .../rushd-reap-orphans_2026-09-24-03-00.json | 11 + ...-t177-reclaim-notice_2026-09-29-02-10.json | 11 + ...06-t203-probe-groups_2026-09-29-08-00.json | 11 + ...t203-stranded-groups_2026-09-29-05-40.json | 11 + ...6-t203-zombie-groups_2026-09-29-06-10.json | 11 + ...-reused-owner-groups_2026-09-29-08-40.json | 11 + .../swarm-r06-t237_2026-09-29-09-15.json | 11 + ...248-operation-marker_2026-09-29-15-30.json | 11 + ...-groups-left-running_2026-09-29-17-50.json | 11 + ...00-reason-order-test_2026-09-29-18-10.json | 11 + ...06-t300-shared-reads_2026-09-29-18-40.json | 11 + ...06-t321-spawn-window_2026-09-29-23-20.json | 11 + ...able-engine-creation_2026-09-29-19-35.json | 11 + ...tput-engine-warnings_2026-09-28-13-52.json | 10 + ...gent-output-log-path_2026-09-28-13-00.json | 11 + .../background-prepare_2026-09-29-22-40.json | 11 + ...lts-before-telemetry_2026-09-29-04-18.json | 10 + ...eration-announcement_2026-09-29-11-45.json | 11 + ...de-code-keeps-daemon_2026-09-29-03-18.json | 10 + ...mand-admission-tests_2026-09-29-12-37.json | 11 + ...-environment-restart_2026-09-29-13-00.json | 11 + ...n-compatible-plugins_2026-09-28-13-00.json | 10 + ...emon-output-contents_2026-09-28-14-55.json | 11 + ...n-output-digest-pool_2026-09-28-15-40.json | 11 + ...n-plugin-environment_2026-09-28-14-10.json | 10 + ...-repeated-path-entry_2026-09-29-00-00.json | 10 + ...on-request-telemetry_2026-09-28-13-25.json | 10 + ...sharing-names-setter_2026-09-29-15-01.json | 11 + .../daemon-shutdown-log_2026-09-29-01-40.json | 10 + ...eferred-cache-writes_2026-09-29-18-45.json | 11 + ...result-measures-test_2026-09-29-06-44.json | 11 + ...t-summary-unfinished_2026-09-29-02-24.json | 10 + ...lt-telemetry-settles_2026-09-29-01-54.json | 10 + ...n-flag-in-place-test_2026-09-29-22-11.json | 11 + ...utation-keeps-daemon_2026-09-29-19-25.json | 11 + ...ix-main-summary-race_2026-09-24-21-45.json | 11 + ...-restart-arbitration_2026-09-24-01-45.json | 10 + ...ushd-error-surfacing_2026-09-24-01-50.json | 11 + ...process-group-checks_2026-09-24-03-00.json | 10 + .../fix-warmset-cap_2026-09-24-01-00.json | 10 + ...ture-script-deadline_2026-09-29-09-15.json | 11 + .../rush-daemon/flag-reload_2026-09-24.json | 11 + ...arse-name-check-test_2026-09-29-07-04.json | 11 + ...script-outside-watch_2026-09-28-14-05.json | 11 + ...ot-during-validation_2026-09-29-05-45.json | 11 + ...-snapshot-reuse-test_2026-09-29-09-15.json | 11 + ...ispatch-wait-timeout_2026-09-30-02-47.json | 11 + ...-running-batch-begin_2026-09-29-19-15.json | 11 + ...in-running-iteration_2026-09-29-17-05.json | 11 + .../linux-tree-watcher_2026-09-24.json | 10 + ...-fixture-maintenance_2026-09-29-08-44.json | 10 + ...seline-per-operation_2026-09-29-11-45.json | 11 + ...t-fewer-hash-updates_2026-09-29-09-50.json | 11 + ...ingerprint-gaps-test_2026-09-29-09-40.json | 11 + .../output-invalidation_2026-09-23.json | 10 + ...alk-during-reconcile_2026-09-29-09-50.json | 11 + .../ping-omit-warm-set_2026-09-29-09-10.json | 11 + ...ueued-request-resend_2026-09-29-17-10.json | 11 + .../r01-exit-after-stop_2026-09-28-23-45.json | 11 + ...-runtime-folder-file_2026-09-29-04-57.json | 11 + ...01-shutdown-deadline_2026-09-28-21-50.json | 11 + .../r01-socket-check_2026-09-29-02-10.json | 11 + ...installation-changed_2026-09-28-18-45.json | 11 + ...n-restart-late-check_2026-09-29-01-45.json | 11 + ...llation-restart-wait_2026-09-28-20-30.json | 11 + .../r03-runtime-folder_2026-09-28-17-15.json | 11 + ...ush-lib-path-handoff_2026-09-28-13-10.json | 11 + ...ontinuing-operations_2026-09-29-19-20.json | 11 + ...08o-stopped-fallback_2026-09-29-20-20.json | 11 + ...6-t177-reap-log-line_2026-09-29-10-36.json | 11 + ...06-t184-nits-escapes_2026-09-29-10-10.json | 11 + ...-failure-and-escapes_2026-09-29-03-00.json | 11 + ...t246-test-successors_2026-09-29-13-05.json | 11 + ...-t320-pool-idle-stop_2026-09-29-22-12.json | 11 + ...t336-pool-idle-tests_2026-09-30-01-45.json | 11 + ...igester-idle-default_2026-09-30-02-26.json | 11 + ...serve-defaults-tests_2026-09-30-03-00.json | 11 + ...nment-restart-reason_2026-09-29-01-30.json | 11 + ...ad-all-before-resend_2026-09-29-20-21.json | 11 + ...ejection-keeps-graph_2026-09-28-13-45.json | 11 + ...-without-debug-lines_2026-09-28-13-30.json | 11 + ...wait-followers-tests_2026-09-29-21-44.json | 11 + ...-lock-wait-followers_2026-09-29-19-00.json | 11 + ...request-results-hook_2026-09-29-01-22.json | 10 + ...rain-default-timeout_2026-09-28-15-08.json | 11 + ...drain-queue-position_2026-09-28-14-20.json | 11 + ...rt-planned-successor_2026-09-28-19-50.json | 11 + ...ugin-skipped-results_2026-09-28-18-39.json | 11 + .../rushd-cancel-hard-abort_2026-09-23.json | 11 + ...ent-gone-doc-comment_2026-09-29-15-05.json | 11 + ...ushd-client-gone-log_2026-09-29-11-50.json | 10 + ...ushd-coalesced-request-env_2026-09-28.json | 11 + .../rushd-env-identity_2026-09-28.json | 11 + ...e-garbage-collection_2026-09-29-04-15.json | 11 + .../rushd-incremental-builds_2026-09-28.json | 11 + .../rushd-lazy-op-env_2026-09-28.json | 11 + ...rushd-operation-request-id_2026-09-28.json | 11 + ...tage-and-retry-delay_2026-09-29-15-56.json | 11 + ...ushd-request-operation-env_2026-09-28.json | 11 + ...rushd-summary-banner_2026-09-24-01-30.json | 11 + ...rushd-telemetry-request-id_2026-09-28.json | 11 + ...nverifiable-changed-inputs_2026-09-28.json | 11 + .../rushx-admission-6085_2026-09-24.json | 10 + ...cript-wait-followers_2026-09-29-21-50.json | 11 + ...stom-phased-commands_2026-09-28-17-52.json | 11 + ...ript-ahead-of-reload_2026-09-29-16-30.json | 10 + ...ased-command-engines_2026-09-28-20-55.json | 11 + ...-queued-request-text_2026-09-29-18-23.json | 11 + ...ed-inputs-comparison_2026-09-28-21-15.json | 11 + ...111-native-lock-wait_2026-09-29-11-57.json | 11 + ...4-t195-closed-output_2026-09-29-06-20.json | 10 + ...-t302-script-passage_2026-09-29-16-45.json | 11 + ...nfiguration-fallback_2026-09-28-16-20.json | 11 + ...8-e2h-selection-move_2026-09-29-22-20.json | 11 + ...rm-r04-t108-fallback_2026-09-28-21-40.json | 11 + ...m-r04-t108-gate-nits_2026-09-29-01-10.json | 11 + ...04-t108-return-early_2026-09-28-20-25.json | 11 + ...-t153-cancel-results_2026-09-28-23-20.json | 11 + ...r04-t170-error-first_2026-09-29-02-05.json | 11 + ...m-r04-t207-int-tests_2026-09-29-08-40.json | 11 + ...rm-r04-t207-int-unit_2026-09-29-09-20.json | 11 + ...ted-before-admission_2026-09-29-05-50.json | 11 + ...306-lock-holder-exit_2026-09-29-17-55.json | 11 + .../swarm-r04-t306-nit1_2026-09-29-21-00.json | 11 + ...31-plugin-shape-test_2026-09-30-00-25.json | 11 + ...ine-shape-lists-test_2026-09-30-01-00.json | 11 + ...e-unknown-names-test_2026-09-30-01-59.json | 11 + ...-stopping-continuing_2026-09-30-04-40.json | 11 + ...pping-already-queued_2026-09-30-06-30.json | 11 + ...4-t69-daemon-silence_2026-09-29-07-20.json | 11 + .../swarm-r04-t69b-int3_2026-09-29-16-30.json | 11 + .../swarm-r04-t69b-nit6_2026-09-29-17-20.json | 11 + .../swarm-r04-t78-nit7_2026-09-29-03-45.json | 11 + ...m-r04-t78-nit8-usage_2026-09-29-21-38.json | 11 + ...tartup-configuration_2026-09-29-01-40.json | 11 + ...-r04-t78-usage-error_2026-09-29-00-14.json | 11 + ...mission-timeout-text_2026-09-28-16-02.json | 10 + ...mission-wait-timeout_2026-09-28-13-45.json | 10 + ...m-r05-cancel-orphans_2026-09-28-13-14.json | 10 + ...identity-parse-reuse_2026-09-29-00-22.json | 10 + ...paration-wait-budget_2026-09-28-18-20.json | 10 + ...estart-drain-recheck_2026-09-28-21-05.json | 10 + ...in-succeeded-capture_2026-09-28-22-05.json | 10 + ...ushx-pending-restart_2026-09-28-16-45.json | 10 + ...-served-rushx-reload_2026-09-28-15-31.json | 10 + ...swarm-r05-stale-join_2026-09-28-14-10.json | 10 + ...eck-environment-test_2026-09-29-04-50.json | 11 + ...n-connecting-clients_2026-09-29-07-05.json | 11 + .../swarm-r05-t166-nits_2026-09-29-05-25.json | 11 + ...restart-wait-reasons_2026-09-29-02-05.json | 11 + ...-installation-change_2026-09-29-04-25.json | 11 + ...transition-admission_2026-09-28-12-45.json | 10 + ...5-warm-set-retention_2026-09-28-12-26.json | 10 + ...thhold-departed-work_2026-09-28-19-20.json | 10 + ...warm-r06-t119-gcflag_2026-09-29-08-50.json | 11 + ...-incremental-command_2026-09-28-23-00.json | 11 + ...248-operation-marker_2026-09-29-15-30.json | 11 + ...-groups-left-running_2026-09-29-17-50.json | 11 + ...swarm-r06-t323-vmrss_2026-09-29-23-50.json | 10 + ...ure-environment-test_2026-09-28-15-47.json | 10 + ...time-capture-sharing_2026-09-28-16-31.json | 10 + ...hared-input-captures_2026-09-28-13-45.json | 10 + ...warm-set-status-once_2026-09-28-12-50.json | 10 + ...e-with-children-test_2026-09-29-04-13.json | 11 + ...efault-deadline-test_2026-09-29-04-13.json | 11 + ...prepare-failure-test_2026-09-30-06-30.json | 11 + ...try-tag-keeps-daemon_2026-09-29-01-38.json | 10 + ...print-no-environment_2026-09-30-08-05.json | 11 + ...ronment-secret-guard_2026-09-30-08-20.json | 11 + ...test-name-references_2026-09-30-07-55.json | 11 + ...t-socket-path-length_2026-09-30-07-55.json | 11 + ...-unavailable-verdict_2026-09-29-16-55.json | 11 + ...ection-verdict-tests_2026-09-29-18-15.json | 11 + ...ase-only-when-needed_2026-09-28-18-45.json | 10 + ...rotected-expiry-test_2026-09-29-01-45.json | 11 + ...03-parent-short-name_2026-09-29-02-40.json | 11 + .../rush/deploy-rush-daemon-dogfood.json | 35 +- .../config/subspaces/default/pnpm-lock.yaml | 26 +- .../config/subspaces/default/repo-state.json | 2 +- common/reviews/api/package-deps-hash.api.md | 14 + common/reviews/api/rush-client-core.api.md | 138 +- .../reviews/api/rush-daemon-protocol.api.md | 101 +- .../reviews/api/rush-daemon-transport.api.md | 53 +- common/reviews/api/rush-daemon.api.md | 141 +- common/reviews/api/rush-lib.api.md | 182 +- docs/rush/dogfooding-rush-daemon.md | 236 +- docs/rush/environment-variables.md | 13 +- docs/rush/plugin-migration-guide.md | 2 + heft-plugins/heft-lint-plugin/src/Eslint.ts | 7 +- .../heft-lint-plugin/src/LintPlugin.ts | 28 +- .../src/schemas/heft-lint-plugin.schema.json | 6 + .../heft-lint-plugin/src/test/Eslint.test.ts | 202 ++ .../src/test/LintPlugin.test.ts | 245 ++ .../src/DeferredWatchFileSystem.ts | 127 +- .../heft-rspack-plugin/src/RspackPlugin.ts | 3 +- .../src/test/DeferredWatchFileSystem.test.ts | 392 ++++ .../heft-typescript-plugin/package.json | 3 +- .../src/TypeScriptBuilder.ts | 6 +- .../src/existenceAwareWatchFile.ts | 132 ++ .../src/loadTypeScriptTool.ts | 4 +- .../src/test/existenceAwareWatchFile.test.ts | 415 ++++ .../src/DeferredWatchFileSystem.test.ts | 391 ++++ .../src/DeferredWatchFileSystem.ts | 127 +- .../src/Webpack4Plugin.ts | 3 +- .../src/DeferredWatchFileSystem.ts | 126 +- .../src/DeleteStaleAssetsPlugin.ts | 206 ++ .../src/Webpack5Plugin.ts | 13 +- .../schemas/heft-webpack5-plugin.schema.json | 5 + .../src/test/DeferredWatchFileSystem.test.ts | 392 ++++ .../src/test/DeleteStaleAssetsPlugin.test.ts | 681 ++++++ .../credential-cache/src/CredentialCache.ts | 47 +- .../src/test/CredentialCache.test.ts | 91 +- libraries/node-core-library/src/LockFile.ts | 453 +++- .../src/test/LockFile.test.ts | 972 +++++++- libraries/operation-graph/src/WatchLoop.ts | 3 +- .../operation-graph/src/protocol.types.ts | 5 +- .../src/readResidentMemoryBytes.ts | 39 + .../src/test/WatchLoop.test.ts | 67 + .../src/test/readResidentMemoryBytes.test.ts | 101 + libraries/package-deps-hash/README.md | 13 + libraries/package-deps-hash/src/FileStamp.ts | 68 + .../package-deps-hash/src/GitIndexFile.ts | 699 ++++++ .../package-deps-hash/src/RepoStateCache.ts | 1070 +++++++++ .../package-deps-hash/src/getRepoState.ts | 297 ++- libraries/package-deps-hash/src/index.ts | 1 + .../src/test/FileStamp.test.ts | 49 + .../src/test/FsmonitorHook.ts | 45 + .../src/test/GitIndexFile.test.ts | 879 +++++++ .../src/test/RepoStateCache.test.ts | 1467 ++++++++++++ .../src/test/UnhashablePaths.test.ts | 309 +++ .../src/test/getRepoDeps.test.ts | 76 +- .../src/test/getRepoState.test.ts | 122 +- libraries/rig-package/src/RigConfig.ts | 2 +- .../rig-package/src/test/RigConfig.test.ts | 26 + libraries/rush-client-core/README.md | 303 ++- .../rush-client-core/src/DaemonClient.ts | 332 ++- .../rush-client-core/src/DaemonClientError.ts | 13 +- .../rush-client-core/src/DaemonDisconnect.ts | 218 ++ .../src/DaemonOwnerDiagnosis.ts | 346 +++ .../rush-client-core/src/DaemonOwnership.ts | 458 ++++ .../src/DaemonRequestEnvironment.ts | 33 + .../src/DaemonRestartCause.ts | 81 + .../src/DaemonRuntimeFolder.ts | 66 + .../rush-client-core/src/DaemonStartup.ts | 136 +- .../src/DaemonStartupReservation.ts | 166 ++ .../src/ExitedDaemonReclaim.ts | 127 + .../src/InheritedFileDescriptors.ts | 77 + .../rush-client-core/src/NativeLockHolder.ts | 120 + .../rush-client-core/src/ProcessStartTime.ts | 109 + .../src/ReclaimedDaemonLog.ts | 195 ++ libraries/rush-client-core/src/StartupLock.ts | 31 +- .../src/StoppedDaemonOwner.ts | 109 + .../src/connectOrAwaitDaemonStartup.ts | 245 ++ .../src/connectOrStartDaemon.ts | 563 ++++- .../src/executeWithDaemonRestart.ts | 398 +++- libraries/rush-client-core/src/index.ts | 37 +- .../rush-client-core/src/runDaemonStartup.ts | 9 + .../test/AbandonedOwnershipReclaim.test.ts | 122 + .../src/test/DaemonArtifactReset.test.ts | 345 +++ .../src/test/DaemonClient.test.ts | 730 +++++- .../src/test/DaemonDisconnect.test.ts | 438 ++++ .../src/test/DaemonOwnerDiagnosis.test.ts | 357 +++ .../src/test/DaemonRestartCause.test.ts | 99 + .../src/test/DaemonRestartFailedError.test.ts | 124 + .../src/test/DaemonStartup.test.ts | 141 ++ .../test/DaemonStartupReadinessPoll.test.ts | 200 ++ .../src/test/ExitedDaemonReclaim.test.ts | 245 ++ .../src/test/InheritedFileDescriptors.test.ts | 198 ++ .../src/test/NativeLockHolder.test.ts | 213 ++ .../src/test/OrphanedOperation.ts | 162 ++ .../src/test/PendingDelays.ts | 40 + .../src/test/ProcessStartTime.test.ts | 43 + .../src/test/QueuedRequestResend.test.ts | 460 ++++ .../src/test/ReclaimedDaemonLog.test.ts | 296 +++ .../src/test/StartupLock.test.ts | 68 +- .../src/test/StoppedDaemonOwner.test.ts | 220 ++ .../src/test/SymlinkedPackageRequire.ts | 63 + .../src/test/UnreapedChildProcess.ts | 26 + .../test/connectOrAwaitDaemonStartup.test.ts | 477 ++++ .../connectOrAwaitDaemonStartupRetry.test.ts | 123 + .../src/test/connectOrStartDaemon.test.ts | 1337 ++++++++++- .../connectOrStartDaemonHelperExit.test.ts | 261 +++ ...tOrStartDaemonInheritedDescriptors.test.ts | 133 ++ .../connectOrStartDaemonLauncherLog.test.ts | 114 + .../connectOrStartDaemonLiveOwner.test.ts | 465 ++++ ...ectOrStartDaemonReservationRelease.test.ts | 266 +++ .../connectOrStartDaemonReusedOwner.test.ts | 137 ++ .../src/test/fixtures/awaitingStarter.ts | 58 + .../src/test/fixtures/daemon.ts | 210 +- .../src/test/fixtures/inheritedDescriptors.ts | 21 + .../src/test/fixtures/startLockHolder.ts | 28 + libraries/rush-daemon-protocol/README.md | 43 +- .../src/ContinuingOperationsValidation.ts | 52 + .../src/ControlMessageValidation.ts | 11 +- .../src/DaemonClientCaps.ts | 5 + .../src/DaemonCommandResult.ts | 16 +- .../src/DaemonControlKinds.ts | 2 + .../src/DaemonControlMessage.ts | 8 +- .../src/DaemonEnvironmentChange.ts | 21 + .../src/DaemonInstallationChange.ts | 62 + .../src/DaemonLifecycleControl.ts | 8 +- .../src/DaemonNativeLockHolder.ts | 14 + .../src/DaemonOperationPayloads.ts | 18 + .../src/DaemonPhasedRequest.ts | 2 + .../src/DaemonPongMessage.ts | 16 + .../src/DaemonPongValidation.ts | 2 + .../src/DaemonProtocolVersion.ts | 26 +- .../src/DaemonRequestAdmission.ts | 108 +- .../src/DaemonRequestAdmissionValidation.ts | 55 + .../src/DaemonRequestControl.ts | 10 + .../src/DaemonRequestEnvelope.ts | 8 + .../src/DaemonWorkspaceInputsChange.ts | 27 + .../src/DaemonWorkspaceStatus.ts | 12 +- .../src/EnvironmentChangeValidation.ts | 18 + .../src/InstallationChangeValidation.ts | 72 + .../src/RequestAdmissionControlValidation.ts | 58 + .../src/RequestControlValidation.ts | 5 +- .../src/RequestEnvelopeValidation.ts | 13 +- .../RequestLifecycleCapabilityValidation.ts | 10 + .../src/RequestResultValidation.ts | 3 + .../src/ShutdownAckValidation.ts | 17 + .../src/SubscribeControlValidation.ts | 4 +- .../src/WorkspaceInputsChangeValidation.ts | 33 + .../src/WorkspaceStatusValidation.ts | 6 + libraries/rush-daemon-protocol/src/index.ts | 39 +- .../src/test/ContinuingOperations.test.ts | 62 + .../src/test/EnvironmentChange.test.ts | 78 + .../src/test/InstallationChange.test.ts | 72 + .../src/test/LifecycleControl.test.ts | 10 + .../src/test/NativeLockHolder.test.ts | 52 + .../test/NativeMutationRestartReason.test.ts | 45 + .../src/test/PingPayload.test.ts | 23 + .../src/test/QueuedRestartReason.test.ts | 51 + .../src/test/RequestAdmission.test.ts | 6 +- .../src/test/RequestResultValidation.test.ts | 16 + .../test/RequestReturnEarlyOnFailure.test.ts | 37 + .../src/test/RequestStartedControl.test.ts | 48 + .../test/WorkspaceInputsRestartReason.test.ts | 82 + libraries/rush-daemon-transport/README.md | 28 +- .../src/DaemonBufferedWrite.ts | 57 + .../src/DaemonExitRelease.ts | 28 + .../src/DaemonFileChange.ts | 55 + .../src/DaemonFileIdentity.ts | 91 + .../src/DaemonFrameConnection.ts | 60 +- .../src/DaemonFrameReceiver.ts | 74 + .../src/DaemonGroupMemberScan.ts | 43 + .../src/DaemonGroupTermination.ts | 53 + .../src/DaemonListener.ts | 44 +- .../src/DaemonListenerBinding.ts | 42 +- .../src/DaemonListenerLifetime.ts | 77 +- .../src/DaemonLockfile.ts | 14 +- .../src/DaemonOperationGroupAdoption.ts | 68 + .../src/DaemonOperationGroupLeftRunning.ts | 92 + .../DaemonOperationGroupLeftRunningMessage.ts | 40 + .../src/DaemonOperationGroupMarker.ts | 37 + .../src/DaemonOperationGroupProof.ts | 46 + .../src/DaemonOperationGroupReaper.ts | 95 + .../src/DaemonOperationGroupRecorder.ts | 76 + .../src/DaemonOperationGroupSpawnHook.ts | 62 + .../src/DaemonOperationGroupSpawnMark.ts | 72 + .../src/DaemonOperationGroupState.ts | 81 + .../src/DaemonOperationGroupSweep.ts | 40 + .../src/DaemonOperationGroups.ts | 88 + .../src/DaemonOrphanReaper.ts | 83 + .../src/DaemonOwnGroup.ts | 22 + .../src/DaemonOwnedEntry.ts | 28 + .../rush-daemon-transport/src/DaemonPaths.ts | 56 +- .../src/DaemonProcessGroup.ts | 90 + .../src/DaemonProcessList.ts | 42 + .../src/DaemonProcessReadsOnce.ts | 37 + .../src/DaemonProcessStat.ts | 82 + .../src/DaemonReapOptions.ts | 98 + .../src/DaemonReclaim.ts | 73 +- .../src/DaemonReclaimMutex.ts | 51 + .../src/DaemonReclaimOptions.ts | 77 + .../src/DaemonReleaseAfter.ts | 32 + .../src/DaemonReusedOwnerReap.ts | 43 + .../src/DaemonRuntimeDir.ts | 59 + .../src/DaemonRuntimeFolderCheck.ts | 67 + .../src/DaemonSocketPathLength.ts | 61 + .../src/DaemonSocketPublication.ts | 97 + .../src/DaemonTransportError.ts | 6 +- .../rush-daemon-transport/src/DaemonUnlink.ts | 22 + libraries/rush-daemon-transport/src/index.ts | 19 +- .../src/test/BackpressureFixture.ts | 62 + .../src/test/BackpressuredSend.test.ts | 95 + .../src/test/ClosedAfterReadingAll.test.ts | 100 + .../test/ClosedAfterReadingAllDiscard.test.ts | 74 + .../test/DaemonOperationGroupReaper.test.ts | 100 + .../test/DaemonOperationGroupRecorder.test.ts | 61 + .../src/test/DaemonOrphanReaper.test.ts | 77 + .../src/test/DaemonPaths.test.ts | 36 +- .../src/test/DaemonProcessGroup.test.ts | 43 + .../src/test/DaemonProcessReadErrors.test.ts | 49 + .../src/test/DaemonProcessStat.test.ts | 77 + .../src/test/ExitReleaseGroup.test.ts | 43 + .../src/test/FakeOperationDaemonFixture.ts | 64 + .../src/test/FakeProcessTable.ts | 71 + .../src/test/FileChange.test.ts | 66 + .../src/test/FileIdentity.test.ts | 94 + .../src/test/GroupMemberScan.test.ts | 97 + .../src/test/LinkedPackageFixture.ts | 87 + .../src/test/ListenerCloseFailure.test.ts | 90 + .../src/test/ListenerExitRelease.test.ts | 90 + .../src/test/ListenerOrphanReap.test.ts | 97 + .../src/test/ListenerReclaimOptions.test.ts | 53 + .../src/test/ListenerSocketCheck.test.ts | 87 + .../src/test/ListenerSuccession.test.ts | 94 + .../src/test/OperationGroupAdoption.test.ts | 50 + .../test/OperationGroupAdoptionEdges.test.ts | 64 + .../src/test/OperationGroupAdoptionFixture.ts | 99 + .../src/test/OperationGroupAdoptionRun.ts | 48 + .../src/test/OperationGroupFixture.ts | 49 + .../test/OperationGroupLeftRunningFixture.ts | 52 + .../OperationGroupLeftRunningMessage.test.ts | 38 + .../OperationGroupLeftRunningOrder.test.ts | 51 + .../OperationGroupLeftRunningProcess.test.ts | 71 + .../OperationGroupLeftRunningReads.test.ts | 56 + .../OperationGroupLeftRunningReason.test.ts | 74 + .../OperationGroupLeftRunningReport.test.ts | 59 + .../src/test/OperationGroupMarker.test.ts | 64 + .../test/OperationGroupMarkerProof.test.ts | 96 + .../src/test/OperationGroupMarkerReap.test.ts | 96 + .../test/OperationGroupReapOnReclaim.test.ts | 89 + .../src/test/OperationGroupSpawnDeath.test.ts | 94 + .../test/OperationGroupSpawnFallback.test.ts | 86 + .../OperationGroupSpawnHookSafety.test.ts | 86 + .../test/OperationGroupSpawnMarkReap.test.ts | 96 + .../test/OperationGroupSpawnMarkWrite.test.ts | 85 + .../src/test/OperationGroupSpawnOrder.test.ts | 96 + .../test/OperationGroupSpawnRecord.test.ts | 78 + .../src/test/OperationGroupSweep.test.ts | 85 + .../src/test/OperationGroupSweepFixture.ts | 59 + .../test/OperationGroupTermination.test.ts | 99 + .../test/OperationMarkerInheritance.test.ts | 41 + .../src/test/OrphanReapOnReclaim.test.ts | 92 + .../src/test/OrphanReaperFixture.ts | 79 + .../src/test/ProcFileReads.test.ts | 49 + .../src/test/ProcessWaitFixture.ts | 63 + .../src/test/RecordOwnership.test.ts | 97 + .../src/test/ReleaseAfter.test.ts | 41 + .../src/test/RequireAfterSocketStat.test.ts | 54 + .../src/test/ReusedOwnerReap.test.ts | 75 + .../src/test/RuntimeFolder.test.ts | 98 + .../src/test/SendAfterClose.test.ts | 37 + .../src/test/SendFrameWritten.test.ts | 82 + .../src/test/SocketPathLength.test.ts | 80 + .../src/test/SocketPathRefusal.test.ts | 54 + .../src/test/SpawnStandInFixture.ts | 92 + .../src/test/SpawnStandInSteps.ts | 31 + .../src/test/StaleRecordProbe.test.ts | 59 + .../test/StrandedGroupReapOnReclaim.test.ts | 77 + .../src/test/SweepOwnership.test.ts | 53 + .../src/test/TestDaemonFixture.ts | 39 +- .../src/test/TestDaemonKeys.test.ts | 62 + .../src/test/TestDaemonKeys.ts | 56 + .../src/test/Unlink.test.ts | 40 + .../src/test/UnreadingPeerFixture.ts | 100 + .../src/test/UnreapedLeaderFixture.ts | 47 + .../src/test/ZombieOperationGroups.test.ts | 97 + libraries/rush-daemon/README.md | 344 ++- libraries/rush-daemon/config/jest.config.json | 5 +- libraries/rush-daemon/package.json | 3 + .../src/BackgroundPreparationScheduler.ts | 227 ++ .../src/ConnectingClientTracker.ts | 124 + .../rush-daemon/src/DaemonControlSession.ts | 278 ++- .../src/DaemonGraphRequestRouter.ts | 8 +- .../src/DaemonIdleGarbageCollector.ts | 120 + libraries/rush-daemon/src/DaemonIdleTimer.ts | 15 +- .../src/DaemonInstallationMonitor.ts | 102 + .../rush-daemon/src/DaemonOrphanReapLog.ts | 55 + .../src/DaemonRequestDispatcher.ts | 95 +- .../rush-daemon/src/DaemonRequestTelemetry.ts | 225 ++ .../src/DaemonRequestUsageError.ts | 26 + .../rush-daemon/src/DaemonResidentMemory.ts | 36 + .../rush-daemon/src/DaemonShutdownDeadline.ts | 76 + .../src/DaemonShutdownDeadlineError.ts | 93 + .../rush-daemon/src/DaemonShutdownError.ts | 105 + .../rush-daemon/src/DaemonShutdownSignals.ts | 58 + .../rush-daemon/src/DaemonSocketWatch.ts | 36 + .../src/DaemonWireRequestClient.ts | 56 +- .../rush-daemon/src/EngineActivityOptions.ts | 32 + .../rush-daemon/src/EngineTerminalProvider.ts | 87 +- .../src/EnvironmentRestartReason.ts | 58 + .../rush-daemon/src/FreshCaptureCoalescer.ts | 113 + .../src/GlobalCommandExecutionContext.ts | 149 +- .../rush-daemon/src/GlobalCommandRequest.ts | 26 +- .../src/GlobalCommandRequestRouter.ts | 49 +- .../rush-daemon/src/LinuxProcessGroupExit.ts | 108 +- libraries/rush-daemon/src/LinuxTreeWatcher.ts | 269 +++ .../src/NativeMutationInstallationState.ts | 97 + .../rush-daemon/src/NativeMutationRequest.ts | 4 +- .../rush-daemon/src/NativeRepositoryLock.ts | 37 + .../src/OperationOutputFingerprints.ts | 532 +++++ .../rush-daemon/src/OutputFolderDigest.ts | 88 + .../rush-daemon/src/OutputFolderDigestPool.ts | 476 ++++ .../src/OutputFolderDigestWorker.ts | 11 + .../rush-daemon/src/PhasedIterationDemand.ts | 133 ++ .../rush-daemon/src/PhasedIterationJoiner.ts | 535 +++++ .../rush-daemon/src/PhasedRequestClient.ts | 23 +- .../src/PhasedRequestEventMultiplexer.ts | 136 +- .../rush-daemon/src/PhasedRequestEventSink.ts | 224 +- .../rush-daemon/src/PhasedRequestRouter.ts | 1178 ++++++++-- .../rush-daemon/src/PhasedRequestSelection.ts | 143 ++ .../rush-daemon/src/PhasedRequestSummary.ts | 321 +++ .../rush-daemon/src/PhasedRequestTelemetry.ts | 164 ++ .../src/ProductionDaemonRequestResolver.ts | 517 ++++- .../src/ReducingGarbageCollection.ts | 46 + libraries/rush-daemon/src/RequestScheduler.ts | 174 +- .../rush-daemon/src/RequestStartedNotice.ts | 20 + .../src/RushCommandRequestPolicy.ts | 24 +- .../rush-daemon/src/RushDaemonCommandLine.ts | 21 +- libraries/rush-daemon/src/RushDaemonHost.ts | 297 ++- .../rush-daemon/src/RushLibPathHandoff.ts | 30 + .../src/RushXDaemonRequestResolver.ts | 3 + .../src/SelectedDaemonBootstrap.ts | 12 +- .../src/StoppedContinuingOperations.ts | 34 + .../src/VersionSelectedDaemonLauncher.ts | 370 ++- libraries/rush-daemon/src/WarmSetRanking.ts | 4 + .../src/WorkspaceEngineComponentFactory.ts | 254 +- .../src/WorkspaceInputsComparison.ts | 72 + .../src/WorkspaceProcessRestart.ts | 12 +- .../src/WorkspaceRequestAdmission.ts | 1032 ++++++++- .../src/WorkspaceRequestLifecycle.ts | 1494 ++++++++++-- .../src/WorkspaceResolverLifecycle.ts | 16 +- .../src/WorkspaceRestartArbiter.ts | 433 ++++ libraries/rush-daemon/src/WorkspaceSession.ts | 40 + .../src/WorkspaceSessionFileWatcher.ts | 96 +- libraries/rush-daemon/src/WorkspaceStatus.ts | 13 +- libraries/rush-daemon/src/WorkspaceWarmSet.ts | 210 +- libraries/rush-daemon/src/index.ts | 19 + libraries/rush-daemon/src/serveRushDaemon.ts | 199 +- libraries/rush-daemon/src/start.ts | 5 +- .../BackgroundPreparationScheduler.test.ts | 288 +++ .../src/test/BackgroundPrepare.test.ts | 624 +++++ .../src/test/ConnectingClientTracker.test.ts | 166 ++ .../src/test/DaemonClientGone.test.ts | 308 +++ .../src/test/DaemonClosedOutput.test.ts | 169 ++ .../src/test/DaemonGraphFixtureSetup.test.ts | 43 +- .../src/test/DaemonGraphGeneration.test.ts | 3 +- .../src/test/DaemonGraphTestFixture.ts | 52 +- .../test/DaemonIdleGarbageCollection.test.ts | 236 ++ .../test/DaemonIdleGarbageCollector.test.ts | 179 ++ .../src/test/DaemonIdleShutdown.test.ts | 3 + .../src/test/DaemonIdleTimer.test.ts | 33 + .../src/test/DaemonInstallationChange.test.ts | 742 ++++++ .../src/test/DaemonOrphanReapLog.test.ts | 248 ++ .../src/test/DaemonProcessExit.test.ts | 263 +++ .../src/test/DaemonRequestStarted.test.ts | 593 +++++ .../src/test/DaemonRequestUsageError.test.ts | 210 ++ .../DaemonRequestWireEarlyFailure.test.ts | 166 ++ .../src/test/DaemonRequestWireGlobal.test.ts | 131 +- .../src/test/DaemonRequestWirePhased.test.ts | 200 ++ .../test/DaemonRequestWireTestUtilities.ts | 28 +- .../src/test/DaemonResidentMemory.test.ts | 108 + .../src/test/DaemonServeDefaults.test.ts | 76 + .../src/test/DaemonShutdown.test.ts | 75 +- .../test/DaemonShutdownCacheWrites.test.ts | 117 + .../src/test/DaemonShutdownDeadline.test.ts | 253 ++ .../src/test/DaemonShutdownError.test.ts | 87 + .../src/test/DaemonShutdownProcess.test.ts | 206 ++ .../src/test/DaemonShutdownSignals.test.ts | 64 + .../src/test/DaemonSocketLoss.test.ts | 171 ++ .../src/test/DaemonSocketWatch.test.ts | 68 + .../src/test/DaemonTestRuntimeBase.test.ts | 165 ++ .../src/test/DaemonTestRuntimeBase.ts | 66 + .../src/test/DaemonWireRequestClient.test.ts | 63 + .../src/test/EngineActivity.test.ts | 90 + .../src/test/EngineTerminalProvider.test.ts | 112 + .../src/test/EnvironmentRestartReason.test.ts | 112 + .../src/test/FailedNativeMutation.test.ts | 423 ++++ .../src/test/FixtureScriptDeadline.test.ts | 79 + .../src/test/FreshCaptureCoalescer.test.ts | 425 ++++ .../test/GlobalCommandRequestRouter.test.ts | 239 ++ .../src/test/LinuxProcessGroupExit.test.ts | 182 +- .../src/test/LinuxTreeWatcher.test.ts | 295 +++ .../test/NativeConnectingClientJoin.test.ts | 96 + .../test/NativeDeferredCacheWrites.test.ts | 97 + .../test/NativeEngineInvalidationPeek.test.ts | 67 + .../src/test/NativeEngineTestCommands.ts | 16 +- .../src/test/NativeEngineTestFixture.ts | 437 ++++ .../src/test/NativeLockAdmission.test.ts | 651 ++++++ .../test/NativeMutationCleanupFailure.test.ts | 9 +- .../NativeMutationInstallationState.test.ts | 173 ++ .../NativeOutputChangeDuringBuild.test.ts | 57 + .../src/test/NativeRepositoryLock.test.ts | 82 + .../test/OperationOutputFingerprints.test.ts | 666 ++++++ ...rationOutputFingerprintsUnreadable.test.ts | 58 + .../src/test/OutputFolderDigest.test.ts | 98 + .../test/OutputFolderDigestEntries.test.ts | 68 + .../src/test/OutputFolderDigestPool.test.ts | 511 ++++ .../test/OutputFolderDigestPoolIdle.test.ts | 297 +++ .../test/OutputFolderDigestPoolStall.test.ts | 66 + .../src/test/PhasedCommandEngineAbort.test.ts | 208 ++ .../src/test/PhasedIterationDemand.test.ts | 245 ++ .../src/test/PhasedRequestBatching.test.ts | 818 ++++++- .../test/PhasedRequestCancellation.test.ts | 618 +++++ .../test/PhasedRequestEarlyFailure.test.ts | 965 ++++++++ .../PhasedRequestEventMultiplexer.test.ts | 222 +- .../src/test/PhasedRequestEventSink.test.ts | 341 ++- .../src/test/PhasedRequestHook.test.ts | 629 +++++ .../src/test/PhasedRequestInteractive.test.ts | 30 + .../src/test/PhasedRequestJoin.test.ts | 1638 +++++++++++++ .../src/test/PhasedRequestRouter.test.ts | 26 + .../PhasedRequestRouterNativeLock.test.ts | 305 +++ .../test/PhasedRequestRouterTestUtilities.ts | 20 +- .../src/test/PhasedRequestSelection.test.ts | 54 + .../src/test/PhasedRequestSummary.test.ts | 236 ++ .../src/test/PhasedRequestTelemetry.test.ts | 634 +++++ .../ProductionDaemonCustomCommands.test.ts | 183 ++ ...roductionDaemonEnvironmentIdentity.test.ts | 202 +- .../ProductionDaemonNativeLockWait.test.ts | 290 +++ .../test/ProductionDaemonRequestParse.test.ts | 260 +++ .../ProductionDaemonRequestResolver.test.ts | 1243 +++++++--- ...ProductionDaemonUnsupportedCommand.test.ts | 73 + .../test/ReducingGarbageCollection.test.ts | 42 + .../test/RemoveSecretEnvironmentVariables.ts | 12 + .../test/RequestAdmissionIntegration.test.ts | 59 +- .../src/test/RequestScheduler.test.ts | 471 ++++ .../src/test/RestartDrainAdmission.test.ts | 462 ++++ .../src/test/RushCommandRequestPolicy.test.ts | 21 + .../src/test/RushDaemonCommandLine.test.ts | 34 +- .../src/test/RushLibPathHandoff.test.ts | 47 + .../test/SecretEnvironmentVariables.test.ts | 39 + .../src/test/SecretEnvironmentVariables.ts | 22 + .../test/ServedScriptNativeLockWait.test.ts | 510 ++++ .../test/StoppedContinuingOperations.test.ts | 64 + .../src/test/SuccessfulMutationFixture.ts | 15 +- .../src/test/SuccessfulNativeMutation.test.ts | 8 +- .../src/test/TemporaryRepoWorkspaceSession.ts | 31 + .../src/test/TestDaemonHostCleanup.ts | 55 + .../TransitionScriptWaitAdmission.test.ts | 418 ++++ .../VersionSelectedDaemonLauncher.test.ts | 594 ++++- .../src/test/WarmGenerationTestUtilities.ts | 19 +- .../src/test/WarmSetRanking.test.ts | 11 + .../src/test/WarmSetTestFixture.ts | 24 + .../test/WorkspaceEarlyFailureResult.test.ts | 728 ++++++ .../WorkspaceEngineComponentFactory.test.ts | 485 +++- .../src/test/WorkspaceEngineSharing.test.ts | 340 +++ .../WorkspaceEnvironmentRestartReason.test.ts | 193 ++ .../test/WorkspaceInputCaptureReceipt.test.ts | 338 +++ .../test/WorkspaceInputCaptureSharing.test.ts | 148 ++ .../test/WorkspaceInputsComparison.test.ts | 156 ++ .../src/test/WorkspaceLifecycleTestProcess.ts | 14 +- .../test/WorkspacePluginEnvironment.test.ts | 40 + .../WorkspacePreparationAdmission.test.ts | 131 ++ .../test/WorkspaceReloadTierStatus.test.ts | 49 +- .../test/WorkspaceRequestAdmission.test.ts | 591 +++++ .../WorkspaceRequestScopedParameters.test.ts | 52 + .../src/test/WorkspaceRestartArbiter.test.ts | 717 ++++++ .../test/WorkspaceRestartArbitration.test.ts | 104 + .../WorkspaceServedScriptAdmission.test.ts | 1233 ++++++++++ ...kspaceServedScriptBehindTransition.test.ts | 255 ++ .../src/test/WorkspaceSession.test.ts | 10 +- .../test/WorkspaceSessionFileWatcher.test.ts | 25 +- .../test/WorkspaceTransitionAdmission.test.ts | 153 ++ .../test/WorkspaceUnsupportedCommand.test.ts | 178 ++ .../src/test/WorkspaceWarmGeneration.test.ts | 32 + .../src/test/WorkspaceWarmQuiescence.test.ts | 2 + .../src/test/WorkspaceWarmSet.test.ts | 242 +- .../WorkspaceWarmSetMaintenanceCost.test.ts | 400 ++++ .../src/test/WorkspaceWatchPolicy.test.ts | 3 +- .../src/test/fixtures/LingeringDaemon.ts | 83 + .../src/test/fixtures/StuckShutdownDaemon.ts | 77 + .../test/fixtures/SuccessfulMutationDaemon.ts | 3 +- .../common/config/rush/build-cache.json | 13 + libraries/rush-lib/assets/rush-init/rush.json | 8 +- .../src/api/CommandLineConfiguration.ts | 68 +- libraries/rush-lib/src/api/CommandLineJson.ts | 1 + .../rush-lib/src/api/DaemonConfiguration.ts | 136 +- .../src/api/EnvironmentConfiguration.ts | 35 + .../rush-lib/src/api/PhasedCommandEngine.ts | 516 ++++- ...dCommandEngineProjectConfigurationError.ts | 27 + .../src/api/PhasedCommandEngineUsageError.ts | 23 + libraries/rush-lib/src/api/Rush.ts | 6 +- .../src/api/RushProjectConfiguration.ts | 435 +++- .../src/api/WorkspaceInputFingerprint.ts | 481 +++- .../api/test/BuildCacheConfiguration.test.ts | 130 ++ .../api/test/CommandLineConfiguration.test.ts | 30 + .../src/api/test/DaemonConfiguration.test.ts | 153 +- .../api/test/EnvironmentConfiguration.test.ts | 8 + .../rush-lib/src/api/test/FlagFile.test.ts | 2 +- .../src/api/test/LastInstallFlag.test.ts | 2 +- .../src/api/test/PhasedCommandEngine.test.ts | 813 ++++++- ...asedCommandEngineParameterIdentity.test.ts | 160 ++ .../test/PhasedCommandEngineSharing.test.ts | 314 +++ .../test/PhasedCommandEngineTelemetry.test.ts | 315 +++ .../api/test/RushProjectConfiguration.test.ts | 407 ++++ .../test/WorkspaceInputFingerprint.test.ts | 423 +++- .../src/api/test/WorkspaceRuntimeWalk.test.ts | 53 + .../RushProjectConfiguration.test.ts.snap | 1 + ...command-line-phased-disableBuildCache.json | 22 + .../command-line-phased-unknown-property.json | 22 + .../api/test/jsonFiles/rush-project-base.json | 3 +- .../rush-lib/src/cli/RushCommandLineParser.ts | 56 +- .../rush-lib/src/cli/RushXCommandLine.ts | 24 +- .../src/cli/actions/BaseRushAction.ts | 14 +- .../cli/scriptActions/GlobalScriptAction.ts | 14 +- .../cli/scriptActions/PhasedScriptAction.ts | 505 +++- .../src/cli/test/ClosedOutputReader.test.ts | 239 ++ .../cli/test/RushCommandLineParser.test.ts | 345 ++- ...mmandLineParserDeferredCacheWrites.test.ts | 107 + .../RushCommandLineParserLegacySkip.test.ts | 200 ++ ...dLineParserPhasedDisableBuildCache.test.ts | 119 + ...RushCommandLineParserReporterClose.test.ts | 47 + .../test/RushCommandLineParserWatch.test.ts | 188 ++ .../src/cli/test/RushXCommand.test.ts | 79 + .../CommandLineHelp.test.ts.snap | 5 +- .../common/config/rush/command-line.json | 14 + .../cli/test/legacySkipRepo/a/package.json | 8 + .../cli/test/legacySkipRepo/b/package.json | 11 + .../common/config/rush/npm-shrinkwrap.json | 7 + .../src/cli/test/legacySkipRepo/rush.json | 17 + .../a/package.json | 9 + .../b/package.json | 12 + .../common/config/rush/command-line.json | 36 + .../common/config/rush/npm-shrinkwrap.json | 7 + .../phasedDisableBuildCacheRepo/rush.json | 17 + libraries/rush-lib/src/index.ts | 31 +- .../src/logic/ProjectChangeAnalyzer.ts | 207 +- .../rush-lib/src/logic/ProjectWatcher.ts | 266 ++- .../rush-lib/src/logic/RepositoryLockWait.ts | 182 ++ libraries/rush-lib/src/logic/Telemetry.ts | 56 +- .../buildCache/DeferredCacheEntryWrites.ts | 681 ++++++ .../logic/buildCache/OperationBuildCache.ts | 285 ++- .../logic/buildCache/OutputFolderReceipt.ts | 443 ++++ .../src/logic/buildCache/StagedRestore.ts | 320 +++ .../test/DeferredCacheEntryWrites.test.ts | 814 +++++++ .../OperationBuildCacheDeferredWrites.test.ts | 298 +++ .../test/OperationBuildCacheReceipt.test.ts | 734 ++++++ .../test/OutputFolderReceipt.test.ts | 215 ++ .../buildCache/test/StagedRestore.test.ts | 569 +++++ .../src/logic/incremental/InputsSnapshot.ts | 446 +++- .../incremental/test/InputsSnapshot.test.ts | 431 ++++ .../logic/installManager/InstallHelpers.ts | 119 +- .../logic/operations/AsyncOperationQueue.ts | 168 +- .../operations/CacheableOperationPlugin.ts | 467 +++- .../operations/DaemonWarmWorkerPlugin.ts | 127 + .../src/logic/operations/IOperationGraph.ts | 208 +- .../src/logic/operations/IOperationRunner.ts | 41 + .../IncrementalExecutionGuardPlugin.ts | 814 +++++++ .../operations/IncrementalExecutionState.ts | 145 ++ .../operations/InputFilesStatSignature.ts | 500 ++++ .../src/logic/operations/LegacySkipPlugin.ts | 303 ++- .../logic/operations/OperationEventSink.ts | 52 + .../operations/OperationExecutionRecord.ts | 260 ++- .../src/logic/operations/OperationGraph.ts | 588 +++-- .../operations/OperationInputFilesCheck.ts | 97 + .../operations/OperationMetadataManager.ts | 80 +- .../operations/OperationOutputManifest.ts | 250 ++ .../PhasedCommandEngineExecution.ts | 3 +- .../operations/PhasedCommandTelemetry.ts | 168 ++ .../logic/operations/PhasedOperationPlugin.ts | 144 +- .../operations/RetainedResultVerification.ts | 179 ++ .../operations/ShardedPhaseOperationPlugin.ts | 15 +- .../logic/operations/ShellOperationRunner.ts | 317 ++- .../operations/ShellOperationRunnerPlugin.ts | 72 +- .../operations/WarmWorkerOperationRunner.ts | 753 ++++++ .../test/AsyncOperationQueue.test.ts | 334 ++- .../test/CacheableOperationPlugin.test.ts | 944 +++++++- ...ableOperationPluginRetainedResults.test.ts | 1208 ++++++++++ .../IncrementalExecutionGuardPlugin.test.ts | 2046 +++++++++++++++++ .../test/InputFilesStatSignature.test.ts | 558 +++++ .../operations/test/LegacySkipPlugin.test.ts | 815 +++++++ .../test/OperationExecutionRecord.test.ts | 280 ++- .../test/OperationGraphEventSink.test.ts | 120 +- .../test/OperationGraphExtension.test.ts | 739 ++++++ ...OperationGraphOperationEnvironment.test.ts | 228 ++ .../test/OperationGraphStateHash.test.ts | 191 ++ .../test/OperationGraphTelemetry.test.ts | 88 + .../test/OperationMetadataManager.test.ts | 162 +- .../test/OperationOutputManifest.test.ts | 208 ++ ...asedOperationPluginRetainedResults.test.ts | 740 ++++++ .../test/ShardedPhaseOperationPlugin.test.ts | 167 ++ .../test/ShellOperationRunnerAbort.test.ts | 199 ++ ...ellOperationRunnerIncrementalGuard.test.ts | 213 ++ .../test/ShellOperationRunnerPlugin.test.ts | 380 ++- ...TrimRushEnvironmentVariablesPlugin.test.ts | 2 + .../test/WarmWorkerOperationRunner.test.ts | 971 ++++++++ .../src/logic/test/InstallHelpers.test.ts | 172 +- .../logic/test/ProjectChangeAnalyzer.test.ts | 330 ++- .../src/logic/test/ProjectWatcher.test.ts | 859 +++++++ .../src/logic/test/RepositoryLockWait.test.ts | 236 ++ .../rush-lib/src/logic/test/Telemetry.test.ts | 47 +- .../logic/test/TelemetryMachineInfo.test.ts | 105 + .../a/config/rush-project.json | 11 + .../test/shardedParameterRepo/a/package.json | 8 + .../common/config/rush/command-line.json | 13 + .../logic/test/shardedParameterRepo/rush.json | 12 + .../pluginFramework/OperationGraphHooks.ts | 78 +- .../pluginFramework/PhasedCommandHookTaps.ts | 72 + .../PluginLoader/AutoinstallerPluginLoader.ts | 12 + .../PluginLoader/PluginLoaderBase.ts | 25 +- .../src/pluginFramework/PluginManager.ts | 119 +- .../test/PhasedCommandHookTaps.test.ts | 86 + .../test/PluginManager.test.ts | 129 ++ .../src/schemas/build-cache.schema.json | 6 + .../src/schemas/command-line.schema.json | 1 + .../schemas/rush-plugin-manifest.schema.json | 8 + .../src/schemas/rush-project.schema.json | 5 + .../rush-lib/src/schemas/rush.schema.json | 49 +- .../src/utilities/FileContentStamp.ts | 36 + .../src/utilities/JsonFileLoadCache.ts | 78 + .../src/utilities/RushLibPathHandoff.ts | 106 + .../rush-lib/src/utilities/SetRushLibPath.ts | 22 +- .../src/utilities/StandardOutputClosure.ts | 162 ++ .../rush-lib/src/utilities/TarExecutable.ts | 43 +- libraries/rush-lib/src/utilities/Utilities.ts | 16 +- .../src/utilities/executionUtilities.ts | 38 + .../src/utilities/runDuringChecksAsync.ts | 29 + .../rush-lib/src/utilities/streamUtilities.ts | 51 + .../utilities/test/FileContentStamp.test.ts | 46 + .../utilities/test/JsonFileLoadCache.test.ts | 147 ++ .../utilities/test/RushLibPathHandoff.test.ts | 228 ++ .../test/StandardOutputClosure.test.ts | 131 ++ .../src/utilities/test/TarExecutable.test.ts | 113 + .../src/utilities/test/Utilities.test.ts | 50 + .../utilities/test/executionUtilities.test.ts | 93 + .../test/runDuringChecksAsync.test.ts | 103 + .../utilities/test/streamUtilities.test.ts | 132 ++ .../test/__snapshots__/script.test.ts.snap | 9 +- .../providers/CommandLineParameterProvider.ts | 21 +- .../test/AmbiguousCommandLineParser.test.ts | 165 ++ .../src/AzureStorageAuthentication.ts | 12 +- .../src/AzureStorageBuildCacheProvider.ts | 107 +- .../src/RushAzureStorageBuildCachePlugin.ts | 7 + .../azure-blob-storage-config.schema.json | 5 +- .../test/AzureBlobStorageConfigSchema.test.ts | 72 + .../AzureStorageBuildCacheProvider.test.ts | 341 ++- .../rush-bridge-cache-plugin/README.md | 3 + .../rush-bridge-cache-plugin/package.json | 1 + .../src/BridgeCachePlugin.ts | 7 +- .../src/test/BridgeCachePlugin.test.ts | 217 ++ 1247 files changed, 125379 insertions(+), 3391 deletions(-) create mode 100644 apps/heft/src/cli/HeftCommandLineParser.test.ts create mode 100644 apps/heft/src/utilities/test/CliUtilities.test.ts create mode 100644 apps/heft/src/utilities/test/WatchFileSystemAdapter.test.ts create mode 100644 apps/rush-cli-client/src/AgentNotices.ts create mode 100644 apps/rush-cli-client/src/AgentOperationTracker.ts create mode 100644 apps/rush-cli-client/src/AgentProgressRenderer.ts create mode 100644 apps/rush-cli-client/src/OperationOutputExcerpt.ts create mode 100644 apps/rush-cli-client/src/clientCancellation.ts create mode 100644 apps/rush-cli-client/src/clientOutput.ts create mode 100644 apps/rush-cli-client/src/continuingOperations.ts create mode 100644 apps/rush-cli-client/src/daemonReclaimNotice.ts create mode 100644 apps/rush-cli-client/src/daemonRestartNotice.ts create mode 100644 apps/rush-cli-client/src/daemonSilence.ts create mode 100644 apps/rush-cli-client/src/earlyRouting.ts create mode 100644 apps/rush-cli-client/src/inProcessFallback.ts create mode 100644 apps/rush-cli-client/src/inProcessLockWait.ts create mode 100644 apps/rush-cli-client/src/nativeLockWaitNotice.ts create mode 100644 apps/rush-cli-client/src/outputSelection.ts create mode 100644 apps/rush-cli-client/src/resultDiagnostics.ts create mode 100644 apps/rush-cli-client/src/terminalColumns.ts create mode 100644 apps/rush-cli-client/src/test/AgentNotices.test.ts create mode 100644 apps/rush-cli-client/src/test/AgentProgressRenderer.test.ts create mode 100644 apps/rush-cli-client/src/test/OperationOutputExcerpt.test.ts create mode 100644 apps/rush-cli-client/src/test/ShutdownExitDaemonTestProcess.ts create mode 100644 apps/rush-cli-client/src/test/TestProcessEnvironment.ts create mode 100644 apps/rush-cli-client/src/test/cancellationNotice.test.ts create mode 100644 apps/rush-cli-client/src/test/clientOutput.test.ts create mode 100644 apps/rush-cli-client/src/test/daemonLiveOwner.test.ts create mode 100644 apps/rush-cli-client/src/test/daemonReclaimNotice.test.ts create mode 100644 apps/rush-cli-client/src/test/daemonRestartNotice.test.ts create mode 100644 apps/rush-cli-client/src/test/daemonSilence.test.ts create mode 100644 apps/rush-cli-client/src/test/daemonSilenceOutput.test.ts create mode 100644 apps/rush-cli-client/src/test/earlyRouting.test.ts create mode 100644 apps/rush-cli-client/src/test/inProcessFallback.test.ts create mode 100644 apps/rush-cli-client/src/test/inProcessLockWait.test.ts create mode 100644 apps/rush-cli-client/src/test/launchClientResultStderr.test.ts create mode 100644 apps/rush-cli-client/src/test/nativeLockWaitNotice.test.ts create mode 100644 apps/rush-cli-client/src/test/omitWarmSetStatus.test.ts create mode 100644 apps/rush-cli-client/src/test/outputSelection.test.ts create mode 100644 apps/rush-cli-client/src/test/resultDiagnostics.test.ts create mode 100644 apps/rush-cli-client/src/test/returnEarlyOnFailure.test.ts create mode 100644 apps/rush-cli-client/src/test/rushxFallbackLines.test.ts create mode 100644 apps/rush-cli-client/src/test/sentBackFallback.test.ts create mode 100644 apps/rush-cli-client/src/test/startupPendingFallback.test.ts create mode 100644 apps/rush-cli-client/src/test/terminalColumns.test.ts create mode 100644 common/changes/@microsoft/rush/abortable-engine-creation_2026-09-29-19-35.json create mode 100644 common/changes/@microsoft/rush/azure-cache-credential-refresh_2026-09-28-21-50.json create mode 100644 common/changes/@microsoft/rush/azure-storage-endpoint-credential_2026-09-29-00-29.json create mode 100644 common/changes/@microsoft/rush/azure-storage-endpoint-schema_2026-09-28-22-28.json create mode 100644 common/changes/@microsoft/rush/background-prepare-option_2026-09-29-22-40.json create mode 100644 common/changes/@microsoft/rush/batched-iteration-announcement_2026-09-29-11-45.json create mode 100644 common/changes/@microsoft/rush/before-log-request_2026-09-29-14-30.json create mode 100644 common/changes/@microsoft/rush/bridge-cache-selected-operations_2026-09-28-18-45.json create mode 100644 common/changes/@microsoft/rush/build-cache-output-receipt_2026-09-29-18-00.json create mode 100644 common/changes/@microsoft/rush/build-cache-receipt-more-tests_2026-09-29-21-12.json create mode 100644 common/changes/@microsoft/rush/build-cache-receipt-tests_2026-09-29-19-08.json create mode 100644 common/changes/@microsoft/rush/build-cache-staged-restore-tests_2026-09-29-21-58.json create mode 100644 common/changes/@microsoft/rush/build-cache-staged-restore_2026-09-29-18-58.json create mode 100644 common/changes/@microsoft/rush/capture-input-files-late_2026-09-29-12-56.json create mode 100644 common/changes/@microsoft/rush/claude-code-keeps-daemon_2026-09-29-03-18.json create mode 100644 common/changes/@microsoft/rush/closed-output-reader_2026-09-29-11-50.json create mode 100644 common/changes/@microsoft/rush/command-agnostic-plugins_2026-09-28-22-40.json create mode 100644 common/changes/@microsoft/rush/daemon-compatible-plugins_2026-09-28-13-00.json create mode 100644 common/changes/@microsoft/rush/daemon-request-telemetry_2026-09-28-13-25.json create mode 100644 common/changes/@microsoft/rush/daemon-sharing-names-parameters_2026-09-28-23-02.json create mode 100644 common/changes/@microsoft/rush/daemon-sharing-names-setter_2026-09-29-15-01.json create mode 100644 common/changes/@microsoft/rush/daemon-telemetry-flush-wait_2026-09-28-19-20.json create mode 100644 common/changes/@microsoft/rush/deferred-cache-writes-tests_2026-09-30-02-38.json create mode 100644 common/changes/@microsoft/rush/deferred-cache-writes_2026-09-29-18-45.json create mode 100644 common/changes/@microsoft/rush/engine-phase-closure-plugin-tests_2026-09-29-12-37.json create mode 100644 common/changes/@microsoft/rush/fix-build-cache-poisoning-race_2026-09-24.json create mode 100644 common/changes/@microsoft/rush/fix-warmset-cap_2026-09-24-01-00.json create mode 100644 common/changes/@microsoft/rush/flag-reload_2026-09-24.json create mode 100644 common/changes/@microsoft/rush/in-process-snapshot-hash-check_2026-09-29-15-16.json create mode 100644 common/changes/@microsoft/rush/incremental-guard-input-files_2026-09-29-08-46.json create mode 100644 common/changes/@microsoft/rush/incremental-inputs-snapshot_2026-09-29-04-15.json create mode 100644 common/changes/@microsoft/rush/initial-script-outside-watch_2026-09-28-14-05.json create mode 100644 common/changes/@microsoft/rush/inputs-snapshot-during-checks_2026-09-29-05-45.json create mode 100644 common/changes/@microsoft/rush/join-captured-input-files-start_2026-09-29-21-59.json create mode 100644 common/changes/@microsoft/rush/join-running-batch-begin_2026-09-29-19-15.json create mode 100644 common/changes/@microsoft/rush/join-running-batch-option_2026-09-29-17-55.json create mode 100644 common/changes/@microsoft/rush/join-running-dispatched-inputs_2026-09-29-21-15.json create mode 100644 common/changes/@microsoft/rush/join-running-iteration_2026-09-29-14-20.json create mode 100644 common/changes/@microsoft/rush/lazy-cache-disabled-reason_2026-09-28-23-08.json create mode 100644 common/changes/@microsoft/rush/legacy-skip-input-files_2026-09-29-10-18.json create mode 100644 common/changes/@microsoft/rush/m03-t179-cpu-info_2026-09-29-07-52.json create mode 100644 common/changes/@microsoft/rush/m03-t182-flag-temp-folders_2026-09-29-08-17.json create mode 100644 common/changes/@microsoft/rush/m03-t215-critical-path_2026-09-29-06-55.json create mode 100644 common/changes/@microsoft/rush/quote-custom-parameter-values_2026-09-30-05-35.json create mode 100644 common/changes/@microsoft/rush/r02-t140-int6-ipc-engine-sharing_2026-09-29-08-55.json create mode 100644 common/changes/@microsoft/rush/r03-legacy-skip-disabled_2026-09-29-03-58.json create mode 100644 common/changes/@microsoft/rush/r03-legacy-skip-invalidation_2026-09-29-03-30.json create mode 100644 common/changes/@microsoft/rush/r03-parent-short-name_2026-09-29-02-40.json create mode 100644 common/changes/@microsoft/rush/r03-runtime-folder_2026-09-28-17-15.json create mode 100644 common/changes/@microsoft/rush/r03-rush-lib-path-handoff_2026-09-28-13-10.json create mode 100644 common/changes/@microsoft/rush/r03-socket-path-length_2026-09-28-19-19.json create mode 100644 common/changes/@microsoft/rush/r03-t201-stale-package-manager-lock_2026-09-29-07-30.json create mode 100644 common/changes/@microsoft/rush/r03-t205-phased-disableBuildCache_2026-09-29-06-24.json create mode 100644 common/changes/@microsoft/rush/repository-lock-wait-prettier_2026-09-29-17-57.json create mode 100644 common/changes/@microsoft/rush/repository-lock-wait-test-cap_2026-09-29-15-40.json create mode 100644 common/changes/@microsoft/rush/request-results-hook_2026-09-29-01-22.json create mode 100644 common/changes/@microsoft/rush/retain-plugin-skipped-results_2026-09-28-18-39.json create mode 100644 common/changes/@microsoft/rush/reuse-unchanged-inputs_2026-09-28-18-30.json create mode 100644 common/changes/@microsoft/rush/reuse-unchanged-repo-state_2026-09-28-21-15.json create mode 100644 common/changes/@microsoft/rush/reuse-unchanged-state-hashes_2026-09-29-07-04.json create mode 100644 common/changes/@microsoft/rush/rush-sdk-export-snapshot_2026-09-29-13-45.json create mode 100644 common/changes/@microsoft/rush/rushd-cancel-hard-abort_2026-09-23.json create mode 100644 common/changes/@microsoft/rush/rushd-env-identity_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-incremental-builds_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-incremental-plugin-runners_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-iteration-hook-env_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-operation-env_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-operation-request-id_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-request-operation-env_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-snapshot-window-input-guard_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-summary-banner_2026-09-24-01-30.json create mode 100644 common/changes/@microsoft/rush/rushd-unverifiable-changed-inputs_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/rushd-warm-workers-disabled-operation-test_2026-09-29.json create mode 100644 common/changes/@microsoft/rush/rushd-warm-workers_2026-09-28.json create mode 100644 common/changes/@microsoft/rush/serve-custom-phased-commands_2026-09-28-17-52.json create mode 100644 common/changes/@microsoft/rush/share-phased-command-engines_2026-09-28-20-55.json create mode 100644 common/changes/@microsoft/rush/swarm-o03-t157-cache-hit-error-log_2026-09-29-00-20.json create mode 100644 common/changes/@microsoft/rush/swarm-o04-t245-in-process-lock-wait_2026-09-29-13-55.json create mode 100644 common/changes/@microsoft/rush/swarm-o05-t204-flush-before-exit_2026-09-29-05-08.json create mode 100644 common/changes/@microsoft/rush/swarm-r01-engine-missing-shrinkwrap-deps_2026-09-28-16-20.json create mode 100644 common/changes/@microsoft/rush/swarm-r01-engine-project-configuration-error_2026-09-28-16-20.json create mode 100644 common/changes/@microsoft/rush/swarm-r01-release-package-manager-lock_2026-09-28-17-58.json create mode 100644 common/changes/@microsoft/rush/swarm-r01-t216-watch-run-requests_2026-09-29-09-15.json create mode 100644 common/changes/@microsoft/rush/swarm-r01-t243-watch-edits-during-iteration_2026-09-29-10-50.json create mode 100644 common/changes/@microsoft/rush/swarm-r01-t268-watch-provider-test_2026-09-29-13-00.json create mode 100644 common/changes/@microsoft/rush/swarm-r02-t298-warned-base_2026-09-29-16-26.json create mode 100644 common/changes/@microsoft/rush/swarm-r04-t78-erroropts_2026-09-29-03-57.json create mode 100644 common/changes/@microsoft/rush/swarm-r04-t78-nit8-usage_2026-09-29-21-38.json create mode 100644 common/changes/@microsoft/rush/swarm-r04-t78-usage-error_2026-09-29-00-14.json create mode 100644 common/changes/@microsoft/rush/swarm-r04-t97-served-script-end_2026-09-29-05-10.json create mode 100644 common/changes/@microsoft/rush/swarm-r05-engine-json-load-cache_2026-09-28-23-50.json create mode 100644 common/changes/@microsoft/rush/swarm-r05-nested-config-project_2026-09-28-12-25.json create mode 100644 common/changes/@microsoft/rush/swarm-r05-t166-restart-wait-reasons_2026-09-29-02-05.json create mode 100644 common/changes/@microsoft/rush/swarm-r05-warm-set-retention_2026-09-28-12-26.json create mode 100644 common/changes/@microsoft/rush/swarm-r06-link-input-stats_2026-09-30-08-20.json create mode 100644 common/changes/@microsoft/rush/swarm-r06-t248-operation-marker_2026-09-29-15-30.json create mode 100644 common/changes/@microsoft/rush/swarm-r07-retained-result-trust_2026-09-28-14-10.json create mode 100644 common/changes/@microsoft/rush/swarm-r07-rig-profile-realpath_2026-09-28-13-00.json create mode 100644 common/changes/@microsoft/rush/swarm-r07-verify-retained-results_2026-09-28-15-25.json create mode 100644 common/changes/@microsoft/rush/telemetry-tag-keeps-daemon_2026-09-29-01-38.json create mode 100644 common/changes/@microsoft/rush/test-name-references_2026-09-30-07-55.json create mode 100644 common/changes/@microsoft/rush/unselected-dependency-blocks-cache-writes_2026-09-29-07-40.json create mode 100644 common/changes/@microsoft/rush/verified-plugin-skips_2026-09-29-17-51.json create mode 100644 common/changes/@microsoft/rush/warm-worker-first-run-folders_2026-09-30-05-40.json create mode 100644 common/changes/@rushstack/credential-cache/credential-cache-redact-parse-error_2026-09-28-22-44.json create mode 100644 common/changes/@rushstack/credential-cache/credential-cache-schema-once_2026-09-28-21-50.json create mode 100644 common/changes/@rushstack/heft-lint-plugin/lint-in-watch-mode_2026-09-28.json create mode 100644 common/changes/@rushstack/heft-lint-plugin/m03-t236-lint-cache-suppressed_2026-09-29-09-28.json create mode 100644 common/changes/@rushstack/heft-rspack-plugin/heft-rspack-plugin-watch-flush_2026-09-29-11-55.json create mode 100644 common/changes/@rushstack/heft-typescript-plugin/realpath-missing-paths_2026-09-28.json create mode 100644 common/changes/@rushstack/heft-typescript-plugin/watch-missing-root-files_2026-09-29.json create mode 100644 common/changes/@rushstack/heft-webpack4-plugin/heft-webpack4-plugin-watch-flush_2026-09-29-11-55.json create mode 100644 common/changes/@rushstack/heft-webpack5-plugin/deferred-watch-test-node-version_2026-09-29-15-05.json create mode 100644 common/changes/@rushstack/heft-webpack5-plugin/delete-stale-assets-in-watch-mode_2026-09-29-10-25.json create mode 100644 common/changes/@rushstack/heft-webpack5-plugin/heft-webpack5-plugin-watch-flush_2026-09-29-09-18.json create mode 100644 common/changes/@rushstack/heft/heft-watch-close-previous-watcher_2026-09-29-08-00.json create mode 100644 common/changes/@rushstack/heft/heft-watch-skip-changes-a-run-read_2026-09-29-14-45.json create mode 100644 common/changes/@rushstack/heft/heft-watch-tests-for-older-mtime-and-unread-folder_2026-09-29-16-37.json create mode 100644 common/changes/@rushstack/heft/r03-t200-heft-error-exit-code_2026-09-29-07-05.json create mode 100644 common/changes/@rushstack/node-core-library/lockfile-birthtime-tie_2026-09-28.json create mode 100644 common/changes/@rushstack/node-core-library/lockfile-exited-process_2026-09-29.json create mode 100644 common/changes/@rushstack/node-core-library/lockfile-own-start-time_2026-09-28.json create mode 100644 common/changes/@rushstack/node-core-library/lockfile-proc-start-time_2026-09-28.json create mode 100644 common/changes/@rushstack/node-core-library/lockfile-start-time-locale_2026-09-28.json create mode 100644 common/changes/@rushstack/node-core-library/lockfile-time-zone-tests_2026-09-29.json create mode 100644 common/changes/@rushstack/operation-graph/operation-graph-exact-rss_2026-09-29-23-35.json create mode 100644 common/changes/@rushstack/package-deps-hash/content-config-fingerprint_2026-09-29-04-55.json create mode 100644 common/changes/@rushstack/package-deps-hash/git-index-carry-over-tests_2026-09-29-05-10.json create mode 100644 common/changes/@rushstack/package-deps-hash/git-status-first_2026-09-29-05-45.json create mode 100644 common/changes/@rushstack/package-deps-hash/keep-index-caches-on-recopy_2026-09-29-02-37.json create mode 100644 common/changes/@rushstack/package-deps-hash/repo-state-cache-hidden-attributes_2026-09-29-12-05.json create mode 100644 common/changes/@rushstack/package-deps-hash/repo-state-cache-more-tests_2026-09-29-03-15.json create mode 100644 common/changes/@rushstack/package-deps-hash/repo-state-cache-sparse-checkout_2026-09-29-14-52.json create mode 100644 common/changes/@rushstack/package-deps-hash/repo-state-cache_2026-09-28-21-15.json create mode 100644 common/changes/@rushstack/package-deps-hash/swarm-r01-git-error-command_2026-09-28-15-02.json create mode 100644 common/changes/@rushstack/package-deps-hash/swarm-r01-hash-object-rejection_2026-09-28-14-47.json create mode 100644 common/changes/@rushstack/package-deps-hash/swarm-r06-relative-temp-folder_2026-09-30-08-20.json create mode 100644 common/changes/@rushstack/package-deps-hash/swarm-r06-unhashable-paths_2026-09-30-06-07.json create mode 100644 common/changes/@rushstack/rig-package/swarm-o08-d551-t2_2026-09-30-05-38.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-admission-failure-code_2026-09-28-15-08.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-empty-selection_2026-09-28-14-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-error-detail_2026-09-28-13-30.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-output-engine-warnings_2026-09-28-13-52.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-output-scale_2026-09-28-13-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-pipe-status_2026-09-28-14-20.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-reporter_2026-09-24-02-35.json create mode 100644 common/changes/@rushstack/rush-cli-client/agent-startup-wait-owner_2026-09-29.json create mode 100644 common/changes/@rushstack/rush-cli-client/background-prepare-option_2026-09-29-22-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/cancel-before-engine-init_2026-09-28-14-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/daemon-recovery_2026-09-23.json create mode 100644 common/changes/@rushstack/rush-cli-client/daemon-shutdown-log_2026-09-29-01-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/daemon-shutdown-not-cancelled_2026-09-28-13-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/deferred-cache-writes_2026-09-29-18-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/failed-mutation-keeps-daemon_2026-09-29-19-25.json create mode 100644 common/changes/@rushstack/rush-cli-client/fix-rushd-error-surfacing_2026-09-24-01-50.json create mode 100644 common/changes/@rushstack/rush-cli-client/fix-warmset-cap_2026-09-24-01-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/join-dispatch-wait-timeout_2026-09-30-02-47.json create mode 100644 common/changes/@rushstack/rush-cli-client/join-running-batch-option_2026-09-29-17-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/m03-t229-fixture-maintenance_2026-09-29-08-44.json create mode 100644 common/changes/@rushstack/rush-cli-client/ping-omit-warm-set_2026-09-29-09-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/queued-request-resend_2026-09-29-17-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/r02-t140-int6-rushx-test-seams_2026-09-29-08-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-abandoned-reservation-relaunch_2026-09-28-23-50.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-explain-daemon-crash_2026-09-28-15-52.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-installation-changed_2026-09-28-18-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-installation-restart-wait_2026-09-28-20-30.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-runtime-folder_2026-09-28-17-15.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-rush-lib-path-handoff_2026-09-28-13-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-socket-path-length_2026-09-28-19-19.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-startup-reservation_2026-09-28-14-25.json create mode 100644 common/changes/@rushstack/rush-cli-client/r03-stop-resolves-reservation_2026-09-28-15-31.json create mode 100644 common/changes/@rushstack/rush-cli-client/r04-t108o-continuing-wait_2026-09-29-19-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/r04-t108o-early-names_2026-09-29-18-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/r04-t108o-stopped-fallback_2026-09-29-20-20.json create mode 100644 common/changes/@rushstack/rush-cli-client/r06-t184-restart-failure-and-escapes_2026-09-29-03-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/r06-t222-nit1_2026-09-29-09-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/r06-t222-restart-notice-once_2026-09-29-07-20.json create mode 100644 common/changes/@rushstack/rush-cli-client/r06-t88-environment-restart-reason_2026-09-29-01-35.json create mode 100644 common/changes/@rushstack/rush-cli-client/repository-lock-wait-prettier_2026-09-29-17-57.json create mode 100644 common/changes/@rushstack/rush-cli-client/rush-client-restart-wait-with-lock-holder_2026-09-29-15-56.json create mode 100644 common/changes/@rushstack/rush-cli-client/rushd-cancel-hard-abort_2026-09-23.json create mode 100644 common/changes/@rushstack/rush-cli-client/rushd-incremental-builds_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-cli-client/rushd-request-operation-env_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-cli-client/rushd-warm-workers_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-cli-client/rushx-daemon-test-readiness-notice_2026-09-29-11-17.json create mode 100644 common/changes/@rushstack/rush-cli-client/script-wait-followers_2026-09-29-21-50.json create mode 100644 common/changes/@rushstack/rush-cli-client/startup-pending-fallback_2026-09-28-16-58.json create mode 100644 common/changes/@rushstack/rush-cli-client/status-no-daemon_2026-09-28-23-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/stop-while-starting_2026-09-28-21-51.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o04-t111-native-lock-wait_2026-09-29-11-57.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o04-t192-reclaim-log_2026-09-29-03-52.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o04-t199-force-reset-reap_2026-09-29-04-52.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o04-t224-unopenable-launcher-log_2026-09-29-08-28.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o04-t245-in-process-lock-wait_2026-09-29-13-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o05-t226-closed-output_2026-09-29-08-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-o05-t266-daemon-silence-stderr_2026-09-29-12-30.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t108-early-failures_2026-09-28-18-14.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t108-nit1-lock-wait-resubmit_2026-09-29-22-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t108-nits_2026-09-28-21-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t108-return-early_2026-09-28-20-25.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t108o-resubmitted-status_2026-09-29-21-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t132-cancel-notice_2026-09-28-22-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t132-nit1_2026-09-29-03-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t142-operation-error_2026-09-28-22-50.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t165-late-warnings_2026-09-29-02-35.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t170-fallback-line_2026-09-29-02-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t170-nit3_2026-09-29-04-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t297-not-run-line_2026-09-29-16-30.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t297-owner-wait-hint_2026-09-29-16-20.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t297-reap-wait_2026-09-29-16-50.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t297-stop-stopped-owner_2026-09-29-16-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t297-stopped-lock-holder_2026-09-29-16-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t306-lock-holder-exit_2026-09-29-17-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t326-lock-wait-resubmit-tests_2026-09-29-23-48.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t340-readme-served-stop_2026-09-30-02-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t345-stopping-continuing_2026-09-30-04-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t364-silence-names-status_2026-09-30-08-15.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t60-nit2_2026-09-29-07-56.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t60-route-reasons_2026-09-29-03-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t69-daemon-silence_2026-09-29-07-20.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t69b-live-owner_2026-09-29-10-38.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t69b-stopped-owner_2026-09-29-11-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t78-nit8-usage_2026-09-29-21-38.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t83-admission-reason_2026-09-28-18-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t83-startup-wait-line_2026-09-28-20-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r04-t97-served-script-end_2026-09-29-05-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r05-admission-failure-reason_2026-09-28-17-39.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r05-admission-wait-timeout_2026-09-28-13-45.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r05-preparation-wait-budget_2026-09-28-18-20.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r05-t166-nits_2026-09-29-05-25.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r05-t166-restart-wait-reasons_2026-09-29-02-05.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r05-t189-mutation-installation-change_2026-09-29-04-25.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r06-t149-incremental-command_2026-09-28-23-00.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r06-t154-inprocess-reclaim_2026-09-28-23-56.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r06-t177-reclaim-notice_2026-09-29-02-10.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r06-t214-reused-owner-groups_2026-09-29-08-40.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r06-t248-operation-marker_2026-09-29-15-30.json create mode 100644 common/changes/@rushstack/rush-cli-client/swarm-r06-t300-groups-left-running_2026-09-29-17-50.json create mode 100644 common/changes/@rushstack/rush-cli-client/test-name-references_2026-09-30-07-55.json create mode 100644 common/changes/@rushstack/rush-cli-client/zero-size-pty_2026-09-28-13-00.json create mode 100644 common/changes/@rushstack/rush-client-core/daemon-recovery_2026-09-23.json create mode 100644 common/changes/@rushstack/rush-client-core/daemon-startup-retry-tests_2026-09-29.json create mode 100644 common/changes/@rushstack/rush-client-core/fix-restart-arbitration_2026-09-24-01-45.json create mode 100644 common/changes/@rushstack/rush-client-core/fix-rushd-error-surfacing_2026-09-24-01-50.json create mode 100644 common/changes/@rushstack/rush-client-core/liveness-ping-omit-warm-set_2026-09-29-10-20.json create mode 100644 common/changes/@rushstack/rush-client-core/ping-omit-warm-set_2026-09-29-09-10.json create mode 100644 common/changes/@rushstack/rush-client-core/queued-request-resend_2026-09-29-17-10.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-abandoned-reservation-relaunch_2026-09-28-23-50.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-explain-daemon-crash_2026-09-28-15-52.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-installation-changed_2026-09-28-18-45.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-installation-restart-wait_2026-09-28-20-30.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-reservation-takeover-recheck-test_2026-09-29-02-55.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-runtime-folder_2026-09-28-17-15.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-socket-path-length_2026-09-28-19-19.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-startup-reservation_2026-09-28-14-25.json create mode 100644 common/changes/@rushstack/rush-client-core/r03-stop-resolves-reservation_2026-09-28-15-31.json create mode 100644 common/changes/@rushstack/rush-client-core/r04-t108o-continuing-wait_2026-09-29-19-45.json create mode 100644 common/changes/@rushstack/rush-client-core/r06-t184-nits-timeout-test_2026-09-29-10-10.json create mode 100644 common/changes/@rushstack/rush-client-core/r06-t184-restart-failure-and-escapes_2026-09-29-03-00.json create mode 100644 common/changes/@rushstack/rush-client-core/read-all-before-resend_2026-09-29-20-21.json create mode 100644 common/changes/@rushstack/rush-client-core/reset-socket-bigint-stat_2026-09-29-14-55.json create mode 100644 common/changes/@rushstack/rush-client-core/restart-drain-default-timeout_2026-09-28-15-08.json create mode 100644 common/changes/@rushstack/rush-client-core/restart-planned-successor_2026-09-28-19-50.json create mode 100644 common/changes/@rushstack/rush-client-core/script-wait-followers_2026-09-29-21-50.json create mode 100644 common/changes/@rushstack/rush-client-core/startup-burst-test_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-client-core/startup-helper-exit-test_2026-09-29-18-10.json create mode 100644 common/changes/@rushstack/rush-client-core/startup-helper-exit_2026-09-29.json create mode 100644 common/changes/@rushstack/rush-client-core/startup-helper-readiness-poll_2026-09-29.json create mode 100644 common/changes/@rushstack/rush-client-core/startup-pending-fallback_2026-09-28-16-58.json create mode 100644 common/changes/@rushstack/rush-client-core/startup-reservation-release_2026-09-29.json create mode 100644 common/changes/@rushstack/rush-client-core/stop-while-starting_2026-09-28-21-51.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-o04-t111-native-lock-wait_2026-09-29-11-57.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-o04-t192-reclaim-log_2026-09-29-03-52.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-o04-t199-force-reset-reap_2026-09-29-04-52.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-o04-t213-log-line-start_2026-09-29-05-41.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-o04-t224-unopenable-launcher-log_2026-09-29-08-28.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t132-cancel-notice_2026-09-28-22-10.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t297-not-run-line_2026-09-29-16-30.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t297-owner-wait-hint_2026-09-29-16-20.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t297-reap-wait_2026-09-29-16-50.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t297-stop-stopped-owner_2026-09-29-16-10.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t297-stopped-lock-holder_2026-09-29-16-45.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t69-daemon-silence_2026-09-29-07-20.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t69-nit1_2026-09-29-09-10.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t69b-live-owner_2026-09-29-10-38.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r04-t69b-stopped-owner_2026-09-29-11-40.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r05-t166-restart-wait-reasons_2026-09-29-02-05.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r05-t189-mutation-installation-change_2026-09-29-04-25.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t154-inprocess-reclaim_2026-09-28-23-56.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t154-reclaim-after-crash_2026-09-28-23-50.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t177-reclaim-notice_2026-09-29-02-10.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t203-stranded-groups_2026-09-29-05-40.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t214-reused-owner-groups_2026-09-29-08-40.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t299-inherited-descriptors_2026-09-29-16-50.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t300-groups-left-running_2026-09-29-17-50.json create mode 100644 common/changes/@rushstack/rush-client-core/swarm-r06-t300-reclaim-line_2026-09-29-20-05.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/agent-output-engine-warnings_2026-09-28-13-52.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/agent-output-log-path_2026-09-28-13-00.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/fix-restart-arbitration_2026-09-24-18-10.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/fix-rushd-error-surfacing_2026-09-24-01-50.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/ping-omit-warm-set_2026-09-29-09-10.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/queued-request-resend_2026-09-29-17-10.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r03-installation-changed_2026-09-28-18-45.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r03-installation-restart-wait_2026-09-28-20-30.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r03-runtime-folder_2026-09-28-17-15.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r04-t108o-continuing-operations_2026-09-29-18-50.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r06-t184-nits-escapes_2026-09-29-10-10.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r06-t184-restart-failure-and-escapes_2026-09-29-03-00.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/r06-t88-environment-restart-reason_2026-09-29-01-15.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/reload-lock-wait-followers_2026-09-29-19-00.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/restart-drain-default-timeout_2026-09-28-15-08.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/restart-planned-successor_2026-09-28-19-50.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/script-wait-followers_2026-09-29-21-50.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-o04-t111-native-lock-wait_2026-09-29-11-57.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r04-t108-return-early_2026-09-28-20-25.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r04-t345-stopping-continuing_2026-09-30-04-40.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r04-t69-daemon-silence_2026-09-29-07-20.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r04-t78-nit8-usage_2026-09-29-21-38.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r05-t166-restart-wait-reasons_2026-09-29-02-05.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r05-t189-mutation-installation-change_2026-09-29-04-25.json create mode 100644 common/changes/@rushstack/rush-daemon-protocol/swarm-r06-t149-incremental-command_2026-09-28-23-00.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/m03-t209-drain_2026-09-29-06-00.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r01-bigint-socket-stat_2026-09-29-07-40.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r01-runtime-folder-file_2026-09-29-04-57.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r01-shutdown-deadline_2026-09-28-21-50.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r01-socket-check_2026-09-29-02-10.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r03-runtime-folder_2026-09-28-17-15.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r03-socket-path-length_2026-09-28-19-19.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r06-t177-listener-reap-report_2026-09-29-10-36.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r06-t262-test-keys_2026-09-29-11-45.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/r06-t344-spawn-mark-reads-tests_2026-09-30-03-50.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/read-all-before-resend-tests_2026-09-30-01-03.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/read-all-before-resend_2026-09-29-20-21.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/rushd-orphan-groups_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/rushd-reap-orphans_2026-09-24-03-00.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t177-reclaim-notice_2026-09-29-02-10.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t203-probe-groups_2026-09-29-08-00.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t203-stranded-groups_2026-09-29-05-40.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t203-zombie-groups_2026-09-29-06-10.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t214-reused-owner-groups_2026-09-29-08-40.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t237_2026-09-29-09-15.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t248-operation-marker_2026-09-29-15-30.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t300-groups-left-running_2026-09-29-17-50.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t300-reason-order-test_2026-09-29-18-10.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t300-shared-reads_2026-09-29-18-40.json create mode 100644 common/changes/@rushstack/rush-daemon-transport/swarm-r06-t321-spawn-window_2026-09-29-23-20.json create mode 100644 common/changes/@rushstack/rush-daemon/abortable-engine-creation_2026-09-29-19-35.json create mode 100644 common/changes/@rushstack/rush-daemon/agent-output-engine-warnings_2026-09-28-13-52.json create mode 100644 common/changes/@rushstack/rush-daemon/agent-output-log-path_2026-09-28-13-00.json create mode 100644 common/changes/@rushstack/rush-daemon/background-prepare_2026-09-29-22-40.json create mode 100644 common/changes/@rushstack/rush-daemon/batch-results-before-telemetry_2026-09-29-04-18.json create mode 100644 common/changes/@rushstack/rush-daemon/batched-iteration-announcement_2026-09-29-11-45.json create mode 100644 common/changes/@rushstack/rush-daemon/claude-code-keeps-daemon_2026-09-29-03-18.json create mode 100644 common/changes/@rushstack/rush-daemon/custom-command-admission-tests_2026-09-29-12-37.json create mode 100644 common/changes/@rushstack/rush-daemon/custom-command-environment-restart_2026-09-29-13-00.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-compatible-plugins_2026-09-28-13-00.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-output-contents_2026-09-28-14-55.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-output-digest-pool_2026-09-28-15-40.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-plugin-environment_2026-09-28-14-10.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-repeated-path-entry_2026-09-29-00-00.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-request-telemetry_2026-09-28-13-25.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-sharing-names-setter_2026-09-29-15-01.json create mode 100644 common/changes/@rushstack/rush-daemon/daemon-shutdown-log_2026-09-29-01-40.json create mode 100644 common/changes/@rushstack/rush-daemon/deferred-cache-writes_2026-09-29-18-45.json create mode 100644 common/changes/@rushstack/rush-daemon/early-result-measures-test_2026-09-29-06-44.json create mode 100644 common/changes/@rushstack/rush-daemon/early-result-summary-unfinished_2026-09-29-02-24.json create mode 100644 common/changes/@rushstack/rush-daemon/early-result-telemetry-settles_2026-09-29-01-54.json create mode 100644 common/changes/@rushstack/rush-daemon/failed-mutation-flag-in-place-test_2026-09-29-22-11.json create mode 100644 common/changes/@rushstack/rush-daemon/failed-mutation-keeps-daemon_2026-09-29-19-25.json create mode 100644 common/changes/@rushstack/rush-daemon/fix-main-summary-race_2026-09-24-21-45.json create mode 100644 common/changes/@rushstack/rush-daemon/fix-restart-arbitration_2026-09-24-01-45.json create mode 100644 common/changes/@rushstack/rush-daemon/fix-rushd-error-surfacing_2026-09-24-01-50.json create mode 100644 common/changes/@rushstack/rush-daemon/fix-rushd-procfs-process-group-checks_2026-09-24-03-00.json create mode 100644 common/changes/@rushstack/rush-daemon/fix-warmset-cap_2026-09-24-01-00.json create mode 100644 common/changes/@rushstack/rush-daemon/fixture-script-deadline_2026-09-29-09-15.json create mode 100644 common/changes/@rushstack/rush-daemon/flag-reload_2026-09-24.json create mode 100644 common/changes/@rushstack/rush-daemon/identity-parse-name-check-test_2026-09-29-07-04.json create mode 100644 common/changes/@rushstack/rush-daemon/initial-script-outside-watch_2026-09-28-14-05.json create mode 100644 common/changes/@rushstack/rush-daemon/inputs-snapshot-during-validation_2026-09-29-05-45.json create mode 100644 common/changes/@rushstack/rush-daemon/inputs-snapshot-reuse-test_2026-09-29-09-15.json create mode 100644 common/changes/@rushstack/rush-daemon/join-dispatch-wait-timeout_2026-09-30-02-47.json create mode 100644 common/changes/@rushstack/rush-daemon/join-running-batch-begin_2026-09-29-19-15.json create mode 100644 common/changes/@rushstack/rush-daemon/join-running-iteration_2026-09-29-17-05.json create mode 100644 common/changes/@rushstack/rush-daemon/linux-tree-watcher_2026-09-24.json create mode 100644 common/changes/@rushstack/rush-daemon/m03-t229-fixture-maintenance_2026-09-29-08-44.json create mode 100644 common/changes/@rushstack/rush-daemon/output-baseline-per-operation_2026-09-29-11-45.json create mode 100644 common/changes/@rushstack/rush-daemon/output-digest-fewer-hash-updates_2026-09-29-09-50.json create mode 100644 common/changes/@rushstack/rush-daemon/output-fingerprint-gaps-test_2026-09-29-09-40.json create mode 100644 common/changes/@rushstack/rush-daemon/output-invalidation_2026-09-23.json create mode 100644 common/changes/@rushstack/rush-daemon/output-walk-during-reconcile_2026-09-29-09-50.json create mode 100644 common/changes/@rushstack/rush-daemon/ping-omit-warm-set_2026-09-29-09-10.json create mode 100644 common/changes/@rushstack/rush-daemon/queued-request-resend_2026-09-29-17-10.json create mode 100644 common/changes/@rushstack/rush-daemon/r01-exit-after-stop_2026-09-28-23-45.json create mode 100644 common/changes/@rushstack/rush-daemon/r01-runtime-folder-file_2026-09-29-04-57.json create mode 100644 common/changes/@rushstack/rush-daemon/r01-shutdown-deadline_2026-09-28-21-50.json create mode 100644 common/changes/@rushstack/rush-daemon/r01-socket-check_2026-09-29-02-10.json create mode 100644 common/changes/@rushstack/rush-daemon/r03-installation-changed_2026-09-28-18-45.json create mode 100644 common/changes/@rushstack/rush-daemon/r03-installation-restart-late-check_2026-09-29-01-45.json create mode 100644 common/changes/@rushstack/rush-daemon/r03-installation-restart-wait_2026-09-28-20-30.json create mode 100644 common/changes/@rushstack/rush-daemon/r03-runtime-folder_2026-09-28-17-15.json create mode 100644 common/changes/@rushstack/rush-daemon/r03-rush-lib-path-handoff_2026-09-28-13-10.json create mode 100644 common/changes/@rushstack/rush-daemon/r04-t108o-continuing-operations_2026-09-29-19-20.json create mode 100644 common/changes/@rushstack/rush-daemon/r04-t108o-stopped-fallback_2026-09-29-20-20.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t177-reap-log-line_2026-09-29-10-36.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t184-nits-escapes_2026-09-29-10-10.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t184-restart-failure-and-escapes_2026-09-29-03-00.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t246-test-successors_2026-09-29-13-05.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t320-pool-idle-stop_2026-09-29-22-12.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t336-pool-idle-tests_2026-09-30-01-45.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t339-digester-idle-default_2026-09-30-02-26.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t343-serve-defaults-tests_2026-09-30-03-00.json create mode 100644 common/changes/@rushstack/rush-daemon/r06-t88-environment-restart-reason_2026-09-29-01-30.json create mode 100644 common/changes/@rushstack/rush-daemon/read-all-before-resend_2026-09-29-20-21.json create mode 100644 common/changes/@rushstack/rush-daemon/rejection-keeps-graph_2026-09-28-13-45.json create mode 100644 common/changes/@rushstack/rush-daemon/rejection-without-debug-lines_2026-09-28-13-30.json create mode 100644 common/changes/@rushstack/rush-daemon/reload-lock-wait-followers-tests_2026-09-29-21-44.json create mode 100644 common/changes/@rushstack/rush-daemon/reload-lock-wait-followers_2026-09-29-19-00.json create mode 100644 common/changes/@rushstack/rush-daemon/request-results-hook_2026-09-29-01-22.json create mode 100644 common/changes/@rushstack/rush-daemon/restart-drain-default-timeout_2026-09-28-15-08.json create mode 100644 common/changes/@rushstack/rush-daemon/restart-drain-queue-position_2026-09-28-14-20.json create mode 100644 common/changes/@rushstack/rush-daemon/restart-planned-successor_2026-09-28-19-50.json create mode 100644 common/changes/@rushstack/rush-daemon/retain-plugin-skipped-results_2026-09-28-18-39.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-cancel-hard-abort_2026-09-23.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-client-gone-doc-comment_2026-09-29-15-05.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-client-gone-log_2026-09-29-11-50.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-coalesced-request-env_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-env-identity_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-idle-garbage-collection_2026-09-29-04-15.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-incremental-builds_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-lazy-op-env_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-operation-request-id_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-pin-record-stage-and-retry-delay_2026-09-29-15-56.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-request-operation-env_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-summary-banner_2026-09-24-01-30.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-telemetry-request-id_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushd-unverifiable-changed-inputs_2026-09-28.json create mode 100644 common/changes/@rushstack/rush-daemon/rushx-admission-6085_2026-09-24.json create mode 100644 common/changes/@rushstack/rush-daemon/script-wait-followers_2026-09-29-21-50.json create mode 100644 common/changes/@rushstack/rush-daemon/serve-custom-phased-commands_2026-09-28-17-52.json create mode 100644 common/changes/@rushstack/rush-daemon/served-script-ahead-of-reload_2026-09-29-16-30.json create mode 100644 common/changes/@rushstack/rush-daemon/share-phased-command-engines_2026-09-28-20-55.json create mode 100644 common/changes/@rushstack/rush-daemon/shutdown-queued-request-text_2026-09-29-18-23.json create mode 100644 common/changes/@rushstack/rush-daemon/skip-unchanged-inputs-comparison_2026-09-28-21-15.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-o04-t111-native-lock-wait_2026-09-29-11-57.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-o04-t195-closed-output_2026-09-29-06-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-o04-t302-script-passage_2026-09-29-16-45.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r01-project-configuration-fallback_2026-09-28-16-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t108-e2h-selection-move_2026-09-29-22-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t108-fallback_2026-09-28-21-40.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t108-gate-nits_2026-09-29-01-10.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t108-return-early_2026-09-28-20-25.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t153-cancel-results_2026-09-28-23-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t170-error-first_2026-09-29-02-05.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t207-int-tests_2026-09-29-08-40.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t207-int-unit_2026-09-29-09-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t207-unsupported-before-admission_2026-09-29-05-50.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t306-lock-holder-exit_2026-09-29-17-55.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t306-nit1_2026-09-29-21-00.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t331-plugin-shape-test_2026-09-30-00-25.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t334-engine-shape-lists-test_2026-09-30-01-00.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t338-engine-shape-unknown-names-test_2026-09-30-01-59.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t345-stopping-continuing_2026-09-30-04-40.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t361-stopping-already-queued_2026-09-30-06-30.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t69-daemon-silence_2026-09-29-07-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t69b-int3_2026-09-29-16-30.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t69b-nit6_2026-09-29-17-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t78-nit7_2026-09-29-03-45.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t78-nit8-usage_2026-09-29-21-38.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t78-startup-configuration_2026-09-29-01-40.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r04-t78-usage-error_2026-09-29-00-14.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-admission-timeout-text_2026-09-28-16-02.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-admission-wait-timeout_2026-09-28-13-45.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-cancel-orphans_2026-09-28-13-14.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-identity-parse-reuse_2026-09-29-00-22.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-preparation-wait-budget_2026-09-28-18-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-restart-drain-recheck_2026-09-28-21-05.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-retain-succeeded-capture_2026-09-28-22-05.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-rushx-pending-restart_2026-09-28-16-45.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-served-rushx-reload_2026-09-28-15-31.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-stale-join_2026-09-28-14-10.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-t127-nit1-recheck-environment-test_2026-09-29-04-50.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-t135-join-connecting-clients_2026-09-29-07-05.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-t166-nits_2026-09-29-05-25.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-t166-restart-wait-reasons_2026-09-29-02-05.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-t189-mutation-installation-change_2026-09-29-04-25.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-transition-admission_2026-09-28-12-45.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-warm-set-retention_2026-09-28-12-26.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r05-withhold-departed-work_2026-09-28-19-20.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r06-t119-gcflag_2026-09-29-08-50.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r06-t149-incremental-command_2026-09-28-23-00.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r06-t248-operation-marker_2026-09-29-15-30.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r06-t300-groups-left-running_2026-09-29-17-50.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r06-t323-vmrss_2026-09-29-23-50.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r07-input-capture-environment-test_2026-09-28-15-47.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r07-receipt-time-capture-sharing_2026-09-28-16-31.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r07-shared-input-captures_2026-09-28-13-45.json create mode 100644 common/changes/@rushstack/rush-daemon/swarm-r07-warm-set-status-once_2026-09-28-12-50.json create mode 100644 common/changes/@rushstack/rush-daemon/t115-nit1-release-with-children-test_2026-09-29-04-13.json create mode 100644 common/changes/@rushstack/rush-daemon/t115-nit2-default-deadline-test_2026-09-29-04-13.json create mode 100644 common/changes/@rushstack/rush-daemon/t359-background-prepare-failure-test_2026-09-30-06-30.json create mode 100644 common/changes/@rushstack/rush-daemon/telemetry-tag-keeps-daemon_2026-09-29-01-38.json create mode 100644 common/changes/@rushstack/rush-daemon/test-assertions-print-no-environment_2026-09-30-08-05.json create mode 100644 common/changes/@rushstack/rush-daemon/test-environment-secret-guard_2026-09-30-08-20.json create mode 100644 common/changes/@rushstack/rush-daemon/test-name-references_2026-09-30-07-55.json create mode 100644 common/changes/@rushstack/rush-daemon/test-socket-path-length_2026-09-30-07-55.json create mode 100644 common/changes/@rushstack/rush-daemon/version-selection-unavailable-verdict_2026-09-29-16-55.json create mode 100644 common/changes/@rushstack/rush-daemon/version-selection-verdict-tests_2026-09-29-18-15.json create mode 100644 common/changes/@rushstack/rush-daemon/warm-set-lease-only-when-needed_2026-09-28-18-45.json create mode 100644 common/changes/@rushstack/rush-daemon/warm-set-protected-expiry-test_2026-09-29-01-45.json create mode 100644 common/changes/@rushstack/ts-command-line/r03-parent-short-name_2026-09-29-02-40.json create mode 100644 heft-plugins/heft-lint-plugin/src/test/Eslint.test.ts create mode 100644 heft-plugins/heft-lint-plugin/src/test/LintPlugin.test.ts create mode 100644 heft-plugins/heft-rspack-plugin/src/test/DeferredWatchFileSystem.test.ts create mode 100644 heft-plugins/heft-typescript-plugin/src/existenceAwareWatchFile.ts create mode 100644 heft-plugins/heft-typescript-plugin/src/test/existenceAwareWatchFile.test.ts create mode 100644 heft-plugins/heft-webpack4-plugin/src/DeferredWatchFileSystem.test.ts create mode 100644 heft-plugins/heft-webpack5-plugin/src/DeleteStaleAssetsPlugin.ts create mode 100644 heft-plugins/heft-webpack5-plugin/src/test/DeferredWatchFileSystem.test.ts create mode 100644 heft-plugins/heft-webpack5-plugin/src/test/DeleteStaleAssetsPlugin.test.ts create mode 100644 libraries/operation-graph/src/readResidentMemoryBytes.ts create mode 100644 libraries/operation-graph/src/test/readResidentMemoryBytes.test.ts create mode 100644 libraries/package-deps-hash/src/FileStamp.ts create mode 100644 libraries/package-deps-hash/src/GitIndexFile.ts create mode 100644 libraries/package-deps-hash/src/RepoStateCache.ts create mode 100644 libraries/package-deps-hash/src/test/FileStamp.test.ts create mode 100644 libraries/package-deps-hash/src/test/FsmonitorHook.ts create mode 100644 libraries/package-deps-hash/src/test/GitIndexFile.test.ts create mode 100644 libraries/package-deps-hash/src/test/RepoStateCache.test.ts create mode 100644 libraries/package-deps-hash/src/test/UnhashablePaths.test.ts create mode 100644 libraries/rush-client-core/src/DaemonDisconnect.ts create mode 100644 libraries/rush-client-core/src/DaemonOwnerDiagnosis.ts create mode 100644 libraries/rush-client-core/src/DaemonOwnership.ts create mode 100644 libraries/rush-client-core/src/DaemonRequestEnvironment.ts create mode 100644 libraries/rush-client-core/src/DaemonRestartCause.ts create mode 100644 libraries/rush-client-core/src/DaemonRuntimeFolder.ts create mode 100644 libraries/rush-client-core/src/DaemonStartupReservation.ts create mode 100644 libraries/rush-client-core/src/ExitedDaemonReclaim.ts create mode 100644 libraries/rush-client-core/src/InheritedFileDescriptors.ts create mode 100644 libraries/rush-client-core/src/NativeLockHolder.ts create mode 100644 libraries/rush-client-core/src/ProcessStartTime.ts create mode 100644 libraries/rush-client-core/src/ReclaimedDaemonLog.ts create mode 100644 libraries/rush-client-core/src/StoppedDaemonOwner.ts create mode 100644 libraries/rush-client-core/src/connectOrAwaitDaemonStartup.ts create mode 100644 libraries/rush-client-core/src/test/AbandonedOwnershipReclaim.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonArtifactReset.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonDisconnect.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonOwnerDiagnosis.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonRestartCause.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonRestartFailedError.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonStartup.test.ts create mode 100644 libraries/rush-client-core/src/test/DaemonStartupReadinessPoll.test.ts create mode 100644 libraries/rush-client-core/src/test/ExitedDaemonReclaim.test.ts create mode 100644 libraries/rush-client-core/src/test/InheritedFileDescriptors.test.ts create mode 100644 libraries/rush-client-core/src/test/NativeLockHolder.test.ts create mode 100644 libraries/rush-client-core/src/test/OrphanedOperation.ts create mode 100644 libraries/rush-client-core/src/test/PendingDelays.ts create mode 100644 libraries/rush-client-core/src/test/ProcessStartTime.test.ts create mode 100644 libraries/rush-client-core/src/test/QueuedRequestResend.test.ts create mode 100644 libraries/rush-client-core/src/test/ReclaimedDaemonLog.test.ts create mode 100644 libraries/rush-client-core/src/test/StoppedDaemonOwner.test.ts create mode 100644 libraries/rush-client-core/src/test/SymlinkedPackageRequire.ts create mode 100644 libraries/rush-client-core/src/test/UnreapedChildProcess.ts create mode 100644 libraries/rush-client-core/src/test/connectOrAwaitDaemonStartup.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrAwaitDaemonStartupRetry.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrStartDaemonHelperExit.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrStartDaemonInheritedDescriptors.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrStartDaemonLauncherLog.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrStartDaemonLiveOwner.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrStartDaemonReservationRelease.test.ts create mode 100644 libraries/rush-client-core/src/test/connectOrStartDaemonReusedOwner.test.ts create mode 100644 libraries/rush-client-core/src/test/fixtures/awaitingStarter.ts create mode 100644 libraries/rush-client-core/src/test/fixtures/inheritedDescriptors.ts create mode 100644 libraries/rush-client-core/src/test/fixtures/startLockHolder.ts create mode 100644 libraries/rush-daemon-protocol/src/ContinuingOperationsValidation.ts create mode 100644 libraries/rush-daemon-protocol/src/DaemonEnvironmentChange.ts create mode 100644 libraries/rush-daemon-protocol/src/DaemonInstallationChange.ts create mode 100644 libraries/rush-daemon-protocol/src/DaemonNativeLockHolder.ts create mode 100644 libraries/rush-daemon-protocol/src/DaemonRequestAdmissionValidation.ts create mode 100644 libraries/rush-daemon-protocol/src/DaemonWorkspaceInputsChange.ts create mode 100644 libraries/rush-daemon-protocol/src/EnvironmentChangeValidation.ts create mode 100644 libraries/rush-daemon-protocol/src/InstallationChangeValidation.ts create mode 100644 libraries/rush-daemon-protocol/src/ShutdownAckValidation.ts create mode 100644 libraries/rush-daemon-protocol/src/WorkspaceInputsChangeValidation.ts create mode 100644 libraries/rush-daemon-protocol/src/test/ContinuingOperations.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/EnvironmentChange.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/InstallationChange.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/NativeLockHolder.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/NativeMutationRestartReason.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/PingPayload.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/QueuedRestartReason.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/RequestReturnEarlyOnFailure.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/RequestStartedControl.test.ts create mode 100644 libraries/rush-daemon-protocol/src/test/WorkspaceInputsRestartReason.test.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonBufferedWrite.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonExitRelease.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonFileChange.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonFileIdentity.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonFrameReceiver.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonGroupMemberScan.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonGroupTermination.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupAdoption.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupLeftRunning.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupLeftRunningMessage.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupMarker.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupProof.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupReaper.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupRecorder.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupSpawnHook.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupSpawnMark.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupState.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroupSweep.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOperationGroups.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOrphanReaper.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOwnGroup.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonOwnedEntry.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonProcessGroup.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonProcessList.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonProcessReadsOnce.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonProcessStat.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonReapOptions.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonReclaimMutex.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonReclaimOptions.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonReleaseAfter.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonReusedOwnerReap.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonRuntimeDir.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonRuntimeFolderCheck.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonSocketPathLength.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonSocketPublication.ts create mode 100644 libraries/rush-daemon-transport/src/DaemonUnlink.ts create mode 100644 libraries/rush-daemon-transport/src/test/BackpressureFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/BackpressuredSend.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ClosedAfterReadingAll.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ClosedAfterReadingAllDiscard.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/DaemonOperationGroupReaper.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/DaemonOperationGroupRecorder.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/DaemonOrphanReaper.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/DaemonProcessGroup.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/DaemonProcessReadErrors.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/DaemonProcessStat.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ExitReleaseGroup.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/FakeOperationDaemonFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/FakeProcessTable.ts create mode 100644 libraries/rush-daemon-transport/src/test/FileChange.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/FileIdentity.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/GroupMemberScan.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/LinkedPackageFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/ListenerCloseFailure.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ListenerExitRelease.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ListenerOrphanReap.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ListenerReclaimOptions.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ListenerSocketCheck.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ListenerSuccession.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupAdoption.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupAdoptionEdges.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupAdoptionFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupAdoptionRun.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningMessage.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningOrder.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningProcess.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningReads.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningReason.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupLeftRunningReport.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupMarker.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupMarkerProof.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupMarkerReap.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupReapOnReclaim.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnDeath.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnFallback.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnHookSafety.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnMarkReap.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnMarkWrite.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnOrder.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSpawnRecord.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSweep.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupSweepFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationGroupTermination.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OperationMarkerInheritance.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OrphanReapOnReclaim.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/OrphanReaperFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/ProcFileReads.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ProcessWaitFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/RecordOwnership.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ReleaseAfter.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/RequireAfterSocketStat.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/ReusedOwnerReap.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/RuntimeFolder.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/SendAfterClose.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/SendFrameWritten.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/SocketPathLength.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/SocketPathRefusal.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/SpawnStandInFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/SpawnStandInSteps.ts create mode 100644 libraries/rush-daemon-transport/src/test/StaleRecordProbe.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/StrandedGroupReapOnReclaim.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/SweepOwnership.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/TestDaemonKeys.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/TestDaemonKeys.ts create mode 100644 libraries/rush-daemon-transport/src/test/Unlink.test.ts create mode 100644 libraries/rush-daemon-transport/src/test/UnreadingPeerFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/UnreapedLeaderFixture.ts create mode 100644 libraries/rush-daemon-transport/src/test/ZombieOperationGroups.test.ts create mode 100644 libraries/rush-daemon/src/BackgroundPreparationScheduler.ts create mode 100644 libraries/rush-daemon/src/ConnectingClientTracker.ts create mode 100644 libraries/rush-daemon/src/DaemonIdleGarbageCollector.ts create mode 100644 libraries/rush-daemon/src/DaemonInstallationMonitor.ts create mode 100644 libraries/rush-daemon/src/DaemonOrphanReapLog.ts create mode 100644 libraries/rush-daemon/src/DaemonRequestTelemetry.ts create mode 100644 libraries/rush-daemon/src/DaemonRequestUsageError.ts create mode 100644 libraries/rush-daemon/src/DaemonResidentMemory.ts create mode 100644 libraries/rush-daemon/src/DaemonShutdownDeadline.ts create mode 100644 libraries/rush-daemon/src/DaemonShutdownDeadlineError.ts create mode 100644 libraries/rush-daemon/src/DaemonShutdownError.ts create mode 100644 libraries/rush-daemon/src/DaemonShutdownSignals.ts create mode 100644 libraries/rush-daemon/src/DaemonSocketWatch.ts create mode 100644 libraries/rush-daemon/src/EngineActivityOptions.ts create mode 100644 libraries/rush-daemon/src/EnvironmentRestartReason.ts create mode 100644 libraries/rush-daemon/src/FreshCaptureCoalescer.ts create mode 100644 libraries/rush-daemon/src/LinuxTreeWatcher.ts create mode 100644 libraries/rush-daemon/src/NativeMutationInstallationState.ts create mode 100644 libraries/rush-daemon/src/NativeRepositoryLock.ts create mode 100644 libraries/rush-daemon/src/OperationOutputFingerprints.ts create mode 100644 libraries/rush-daemon/src/OutputFolderDigest.ts create mode 100644 libraries/rush-daemon/src/OutputFolderDigestPool.ts create mode 100644 libraries/rush-daemon/src/OutputFolderDigestWorker.ts create mode 100644 libraries/rush-daemon/src/PhasedIterationDemand.ts create mode 100644 libraries/rush-daemon/src/PhasedIterationJoiner.ts create mode 100644 libraries/rush-daemon/src/PhasedRequestSelection.ts create mode 100644 libraries/rush-daemon/src/PhasedRequestSummary.ts create mode 100644 libraries/rush-daemon/src/PhasedRequestTelemetry.ts create mode 100644 libraries/rush-daemon/src/ReducingGarbageCollection.ts create mode 100644 libraries/rush-daemon/src/RequestStartedNotice.ts create mode 100644 libraries/rush-daemon/src/RushLibPathHandoff.ts create mode 100644 libraries/rush-daemon/src/StoppedContinuingOperations.ts create mode 100644 libraries/rush-daemon/src/WorkspaceInputsComparison.ts create mode 100644 libraries/rush-daemon/src/WorkspaceRestartArbiter.ts create mode 100644 libraries/rush-daemon/src/test/BackgroundPreparationScheduler.test.ts create mode 100644 libraries/rush-daemon/src/test/BackgroundPrepare.test.ts create mode 100644 libraries/rush-daemon/src/test/ConnectingClientTracker.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonClientGone.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonClosedOutput.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonIdleGarbageCollection.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonIdleGarbageCollector.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonInstallationChange.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonOrphanReapLog.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonProcessExit.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonRequestStarted.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonRequestUsageError.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonRequestWireEarlyFailure.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonResidentMemory.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonServeDefaults.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonShutdownCacheWrites.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonShutdownDeadline.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonShutdownError.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonShutdownProcess.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonShutdownSignals.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonSocketLoss.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonSocketWatch.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonTestRuntimeBase.test.ts create mode 100644 libraries/rush-daemon/src/test/DaemonTestRuntimeBase.ts create mode 100644 libraries/rush-daemon/src/test/DaemonWireRequestClient.test.ts create mode 100644 libraries/rush-daemon/src/test/EngineActivity.test.ts create mode 100644 libraries/rush-daemon/src/test/EngineTerminalProvider.test.ts create mode 100644 libraries/rush-daemon/src/test/EnvironmentRestartReason.test.ts create mode 100644 libraries/rush-daemon/src/test/FailedNativeMutation.test.ts create mode 100644 libraries/rush-daemon/src/test/FixtureScriptDeadline.test.ts create mode 100644 libraries/rush-daemon/src/test/FreshCaptureCoalescer.test.ts create mode 100644 libraries/rush-daemon/src/test/LinuxTreeWatcher.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeConnectingClientJoin.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeDeferredCacheWrites.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeEngineInvalidationPeek.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeEngineTestFixture.ts create mode 100644 libraries/rush-daemon/src/test/NativeLockAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeMutationInstallationState.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeOutputChangeDuringBuild.test.ts create mode 100644 libraries/rush-daemon/src/test/NativeRepositoryLock.test.ts create mode 100644 libraries/rush-daemon/src/test/OperationOutputFingerprints.test.ts create mode 100644 libraries/rush-daemon/src/test/OperationOutputFingerprintsUnreadable.test.ts create mode 100644 libraries/rush-daemon/src/test/OutputFolderDigest.test.ts create mode 100644 libraries/rush-daemon/src/test/OutputFolderDigestEntries.test.ts create mode 100644 libraries/rush-daemon/src/test/OutputFolderDigestPool.test.ts create mode 100644 libraries/rush-daemon/src/test/OutputFolderDigestPoolIdle.test.ts create mode 100644 libraries/rush-daemon/src/test/OutputFolderDigestPoolStall.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedCommandEngineAbort.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedIterationDemand.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestCancellation.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestEarlyFailure.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestHook.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestJoin.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestRouterNativeLock.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestSelection.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestSummary.test.ts create mode 100644 libraries/rush-daemon/src/test/PhasedRequestTelemetry.test.ts create mode 100644 libraries/rush-daemon/src/test/ProductionDaemonCustomCommands.test.ts create mode 100644 libraries/rush-daemon/src/test/ProductionDaemonNativeLockWait.test.ts create mode 100644 libraries/rush-daemon/src/test/ProductionDaemonRequestParse.test.ts create mode 100644 libraries/rush-daemon/src/test/ProductionDaemonUnsupportedCommand.test.ts create mode 100644 libraries/rush-daemon/src/test/ReducingGarbageCollection.test.ts create mode 100644 libraries/rush-daemon/src/test/RemoveSecretEnvironmentVariables.ts create mode 100644 libraries/rush-daemon/src/test/RestartDrainAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/RushLibPathHandoff.test.ts create mode 100644 libraries/rush-daemon/src/test/SecretEnvironmentVariables.test.ts create mode 100644 libraries/rush-daemon/src/test/SecretEnvironmentVariables.ts create mode 100644 libraries/rush-daemon/src/test/ServedScriptNativeLockWait.test.ts create mode 100644 libraries/rush-daemon/src/test/StoppedContinuingOperations.test.ts create mode 100644 libraries/rush-daemon/src/test/TemporaryRepoWorkspaceSession.ts create mode 100644 libraries/rush-daemon/src/test/TestDaemonHostCleanup.ts create mode 100644 libraries/rush-daemon/src/test/TransitionScriptWaitAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceEarlyFailureResult.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceEngineSharing.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceEnvironmentRestartReason.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceInputCaptureReceipt.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceInputCaptureSharing.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceInputsComparison.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspacePluginEnvironment.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspacePreparationAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceRequestAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceRequestScopedParameters.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceRestartArbiter.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceRestartArbitration.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceServedScriptAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceServedScriptBehindTransition.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceTransitionAdmission.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceUnsupportedCommand.test.ts create mode 100644 libraries/rush-daemon/src/test/WorkspaceWarmSetMaintenanceCost.test.ts create mode 100644 libraries/rush-daemon/src/test/fixtures/LingeringDaemon.ts create mode 100644 libraries/rush-daemon/src/test/fixtures/StuckShutdownDaemon.ts create mode 100644 libraries/rush-lib/src/api/PhasedCommandEngineProjectConfigurationError.ts create mode 100644 libraries/rush-lib/src/api/PhasedCommandEngineUsageError.ts create mode 100644 libraries/rush-lib/src/api/test/BuildCacheConfiguration.test.ts create mode 100644 libraries/rush-lib/src/api/test/PhasedCommandEngineParameterIdentity.test.ts create mode 100644 libraries/rush-lib/src/api/test/PhasedCommandEngineSharing.test.ts create mode 100644 libraries/rush-lib/src/api/test/PhasedCommandEngineTelemetry.test.ts create mode 100644 libraries/rush-lib/src/api/test/WorkspaceRuntimeWalk.test.ts create mode 100644 libraries/rush-lib/src/api/test/jsonFiles/command-line-phased-disableBuildCache.json create mode 100644 libraries/rush-lib/src/api/test/jsonFiles/command-line-phased-unknown-property.json create mode 100644 libraries/rush-lib/src/cli/test/ClosedOutputReader.test.ts create mode 100644 libraries/rush-lib/src/cli/test/RushCommandLineParserDeferredCacheWrites.test.ts create mode 100644 libraries/rush-lib/src/cli/test/RushCommandLineParserLegacySkip.test.ts create mode 100644 libraries/rush-lib/src/cli/test/RushCommandLineParserPhasedDisableBuildCache.test.ts create mode 100644 libraries/rush-lib/src/cli/test/RushCommandLineParserWatch.test.ts create mode 100644 libraries/rush-lib/src/cli/test/RushXCommand.test.ts create mode 100644 libraries/rush-lib/src/cli/test/legacySkipRepo/a/package.json create mode 100644 libraries/rush-lib/src/cli/test/legacySkipRepo/b/package.json create mode 100644 libraries/rush-lib/src/cli/test/legacySkipRepo/common/config/rush/npm-shrinkwrap.json create mode 100644 libraries/rush-lib/src/cli/test/legacySkipRepo/rush.json create mode 100644 libraries/rush-lib/src/cli/test/phasedDisableBuildCacheRepo/a/package.json create mode 100644 libraries/rush-lib/src/cli/test/phasedDisableBuildCacheRepo/b/package.json create mode 100644 libraries/rush-lib/src/cli/test/phasedDisableBuildCacheRepo/common/config/rush/command-line.json create mode 100644 libraries/rush-lib/src/cli/test/phasedDisableBuildCacheRepo/common/config/rush/npm-shrinkwrap.json create mode 100644 libraries/rush-lib/src/cli/test/phasedDisableBuildCacheRepo/rush.json create mode 100644 libraries/rush-lib/src/logic/RepositoryLockWait.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/DeferredCacheEntryWrites.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/OutputFolderReceipt.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/StagedRestore.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/test/DeferredCacheEntryWrites.test.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/test/OperationBuildCacheDeferredWrites.test.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/test/OperationBuildCacheReceipt.test.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/test/OutputFolderReceipt.test.ts create mode 100644 libraries/rush-lib/src/logic/buildCache/test/StagedRestore.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/DaemonWarmWorkerPlugin.ts create mode 100644 libraries/rush-lib/src/logic/operations/IncrementalExecutionGuardPlugin.ts create mode 100644 libraries/rush-lib/src/logic/operations/IncrementalExecutionState.ts create mode 100644 libraries/rush-lib/src/logic/operations/InputFilesStatSignature.ts create mode 100644 libraries/rush-lib/src/logic/operations/OperationInputFilesCheck.ts create mode 100644 libraries/rush-lib/src/logic/operations/OperationOutputManifest.ts create mode 100644 libraries/rush-lib/src/logic/operations/PhasedCommandTelemetry.ts create mode 100644 libraries/rush-lib/src/logic/operations/RetainedResultVerification.ts create mode 100644 libraries/rush-lib/src/logic/operations/WarmWorkerOperationRunner.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/CacheableOperationPluginRetainedResults.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/IncrementalExecutionGuardPlugin.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/InputFilesStatSignature.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/LegacySkipPlugin.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/OperationGraphExtension.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/OperationGraphOperationEnvironment.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/OperationGraphStateHash.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/OperationGraphTelemetry.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/OperationOutputManifest.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/PhasedOperationPluginRetainedResults.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/ShardedPhaseOperationPlugin.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerAbort.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/ShellOperationRunnerIncrementalGuard.test.ts create mode 100644 libraries/rush-lib/src/logic/operations/test/WarmWorkerOperationRunner.test.ts create mode 100644 libraries/rush-lib/src/logic/test/ProjectWatcher.test.ts create mode 100644 libraries/rush-lib/src/logic/test/RepositoryLockWait.test.ts create mode 100644 libraries/rush-lib/src/logic/test/TelemetryMachineInfo.test.ts create mode 100644 libraries/rush-lib/src/logic/test/shardedParameterRepo/a/config/rush-project.json create mode 100644 libraries/rush-lib/src/logic/test/shardedParameterRepo/a/package.json create mode 100644 libraries/rush-lib/src/logic/test/shardedParameterRepo/common/config/rush/command-line.json create mode 100644 libraries/rush-lib/src/logic/test/shardedParameterRepo/rush.json create mode 100644 libraries/rush-lib/src/pluginFramework/PhasedCommandHookTaps.ts create mode 100644 libraries/rush-lib/src/pluginFramework/test/PhasedCommandHookTaps.test.ts create mode 100644 libraries/rush-lib/src/pluginFramework/test/PluginManager.test.ts create mode 100644 libraries/rush-lib/src/utilities/FileContentStamp.ts create mode 100644 libraries/rush-lib/src/utilities/JsonFileLoadCache.ts create mode 100644 libraries/rush-lib/src/utilities/RushLibPathHandoff.ts create mode 100644 libraries/rush-lib/src/utilities/StandardOutputClosure.ts create mode 100644 libraries/rush-lib/src/utilities/runDuringChecksAsync.ts create mode 100644 libraries/rush-lib/src/utilities/streamUtilities.ts create mode 100644 libraries/rush-lib/src/utilities/test/FileContentStamp.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/JsonFileLoadCache.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/RushLibPathHandoff.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/StandardOutputClosure.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/TarExecutable.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/executionUtilities.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/runDuringChecksAsync.test.ts create mode 100644 libraries/rush-lib/src/utilities/test/streamUtilities.test.ts create mode 100644 rush-plugins/rush-azure-storage-build-cache-plugin/src/test/AzureBlobStorageConfigSchema.test.ts create mode 100644 rush-plugins/rush-bridge-cache-plugin/src/test/BridgeCachePlugin.test.ts diff --git a/apps/heft/src/cli/HeftCommandLineParser.test.ts b/apps/heft/src/cli/HeftCommandLineParser.test.ts new file mode 100644 index 00000000000..7be596af5ee --- /dev/null +++ b/apps/heft/src/cli/HeftCommandLineParser.test.ts @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as childProcess from 'node:child_process'; +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; + +jest.setTimeout(30_000); + +const HEFT_START_PATH: string = path.resolve(__dirname, '../start.js'); + +// A project with one task. The task sets process.exitCode to FIXTURE_EXIT_CODE in the Heft process, the +// way a test that Jest runs in band can, and then fails if FIXTURE_FAIL is set. +const FIXTURE_FILES: Record = { + 'package.json': JSON.stringify({ name: 'heft-exit-code-fixture', version: '1.0.0', private: true }), + 'heft-plugin.json': JSON.stringify({ + taskPlugins: [{ pluginName: 'exit-code-fixture-plugin', entryPoint: './plugin.js' }] + }), + 'config/heft.json': JSON.stringify({ + phasesByName: { + test: { tasksByName: { fixture: { taskPlugin: { pluginPackage: 'heft-exit-code-fixture' } } } } + } + }), + 'plugin.js': `module.exports = class { + apply(taskSession) { + taskSession.hooks.run.tapPromise('exit-code-fixture-plugin', async () => { + process.exitCode = Number(process.env.FIXTURE_EXIT_CODE); + if (process.env.FIXTURE_FAIL) { + throw new Error('The fixture task failed on purpose'); + } + }); + } +}; +` +}; + +// The same project, but its heft.json declares an alias with the name of the "test" phase's action, so Heft fails +// before any task runs, with an error that nothing has reported yet. +const ALIAS_CLASH_FIXTURE_FILES: Record = { + ...FIXTURE_FILES, + 'config/heft.json': JSON.stringify({ + ...JSON.parse(FIXTURE_FILES['config/heft.json']), + aliasesByName: { test: { actionName: 'test' } } + }) +}; + +function createFixtureFolder(files: Record): string { + const fixtureFolderPath: string = fs.mkdtempSync(path.join(os.tmpdir(), 'heft-exit-code-')); + for (const [relativePath, contents] of Object.entries(files)) { + const filePath: string = path.join(fixtureFolderPath, relativePath); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, contents); + } + return fixtureFolderPath; +} + +interface IHeftResult { + exitCode: number | undefined; + output: string; +} + +describe('HeftCommandLineParser', () => { + let fixtureFolderPath: string; + let aliasClashFixtureFolderPath: string; + + beforeAll(() => { + fixtureFolderPath = createFixtureFolder(FIXTURE_FILES); + aliasClashFixtureFolderPath = createFixtureFolder(ALIAS_CLASH_FIXTURE_FILES); + }); + + afterAll(() => { + fs.rmSync(fixtureFolderPath, { recursive: true, force: true }); + fs.rmSync(aliasClashFixtureFolderPath, { recursive: true, force: true }); + }); + + async function runHeftTestAsync( + taskExitCode: number, + taskFails: boolean, + folderPath: string = fixtureFolderPath + ): Promise { + const env: NodeJS.ProcessEnv = { ...process.env, FIXTURE_EXIT_CODE: String(taskExitCode) }; + delete env.FIXTURE_FAIL; + delete env._RUSH_REPORTER_CHILD_FD; + delete env._RUSH_REPORTER_CHILD_ACK_FD; + if (taskFails) { + env.FIXTURE_FAIL = '1'; + } + + const child: childProcess.ChildProcess = childProcess.spawn(process.execPath, [HEFT_START_PATH, 'test'], { + cwd: folderPath, + env, + stdio: ['ignore', 'pipe', 'pipe'] + }); + let output: string = ''; + const appendOutput: (chunk: string) => void = (chunk: string) => { + output += chunk; + }; + child.stdout?.setEncoding('utf8').on('data', appendOutput); + child.stderr?.setEncoding('utf8').on('data', appendOutput); + const exitCode: number | null = await new Promise((resolve, reject) => { + child.once('error', reject); + child.once('close', resolve); + }); + + return { exitCode: exitCode ?? undefined, output }; + } + + it('exits with 1 when a task fails after code in the Heft process set process.exitCode to 0', async () => { + const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, true); + expect(output).toContain('The fixture task failed on purpose'); + expect(exitCode).toBe(1); + }); + + it('exits with 1 when a task fails after code in the Heft process set a negative process.exitCode', async () => { + const { exitCode, output }: IHeftResult = await runHeftTestAsync(-1, true); + expect(output).toContain('The fixture task failed on purpose'); + expect(exitCode).toBe(1); + }); + + it('keeps a positive process.exitCode when a task fails', async () => { + const { exitCode, output }: IHeftResult = await runHeftTestAsync(3, true); + expect(output).toContain('The fixture task failed on purpose'); + expect(exitCode).toBe(3); + }); + + it('exits with 0 when the task succeeds after code in the Heft process set process.exitCode to 0', async () => { + const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, false); + expect(output).not.toContain('The fixture task failed on purpose'); + expect(exitCode).toBe(0); + }); + + it('writes an error that nothing reported before it exits, and exits with 1', async () => { + const { exitCode, output }: IHeftResult = await runHeftTestAsync(0, false, aliasClashFixtureFolderPath); + expect(output).toContain( + 'The alias "test" specified in heft.json cannot be used because an action with that name already exists.' + ); + expect(exitCode).toBe(1); + }); +}); diff --git a/apps/heft/src/cli/HeftCommandLineParser.ts b/apps/heft/src/cli/HeftCommandLineParser.ts index 6b7ada39707..7aec0c77f7c 100644 --- a/apps/heft/src/cli/HeftCommandLineParser.ts +++ b/apps/heft/src/cli/HeftCommandLineParser.ts @@ -26,7 +26,7 @@ import { PhaseAction } from './actions/PhaseAction'; import { RunAction } from './actions/RunAction'; import type { IHeftActionOptions } from './actions/IHeftAction'; import { AliasAction } from './actions/AliasAction'; -import { getToolParameterNamesFromArgs } from '../utilities/CliUtilities'; +import { getErrorExitCode, getToolParameterNamesFromArgs } from '../utilities/CliUtilities'; import { Constants } from '../utilities/Constants'; import { HeftChildReporter } from '../pluginFramework/logging/HeftChildReporter'; @@ -271,11 +271,6 @@ export class HeftCommandLineParser extends CommandLineParser { this.globalTerminal.writeErrorLine(error.stack!); } - const exitCode: string | number | undefined = process.exitCode; - if (!exitCode || typeof exitCode !== 'number' || exitCode > 0) { - process.exit(exitCode); - } else { - process.exit(1); - } + process.exit(getErrorExitCode(process.exitCode)); } } diff --git a/apps/heft/src/utilities/CliUtilities.ts b/apps/heft/src/utilities/CliUtilities.ts index d322dc45e1f..d2a0b1e4fb1 100644 --- a/apps/heft/src/utilities/CliUtilities.ts +++ b/apps/heft/src/utilities/CliUtilities.ts @@ -20,3 +20,15 @@ export function getToolParameterNamesFromArgs(argv: string[] = process.argv): Se } return toolParameters; } + +/** + * Get the exit code for a Heft process that is exiting because of an error. This is `exitCode` if it + * is a positive integer, and 1 otherwise, so it is never 0. + * + * @param exitCode - The current value of `process.exitCode`. Code that ran in the Heft process, such as + * a test that Jest ran in band, may have set it to 0. + */ +export function getErrorExitCode(exitCode: string | number | undefined): number { + const numericExitCode: number = Number(exitCode); + return Number.isInteger(numericExitCode) && numericExitCode > 0 ? numericExitCode : 1; +} diff --git a/apps/heft/src/utilities/WatchFileSystemAdapter.ts b/apps/heft/src/utilities/WatchFileSystemAdapter.ts index cc886e24a27..0f6b1718473 100644 --- a/apps/heft/src/utilities/WatchFileSystemAdapter.ts +++ b/apps/heft/src/utilities/WatchFileSystemAdapter.ts @@ -129,6 +129,126 @@ interface ITimeEntry { safeTime: number; } +/** + * What a run read. The run's watcher checks the changes that it reports against this. + */ +interface IRunInputs { + /** + * When the run started. The watcher reports files whose times are close to this time, or later. + */ + baseline: number | undefined; + /** + * The time of each file that the run read, as the run recorded it + */ + files: ReadonlyMap; + /** + * The folders that the run read + */ + contexts: ReadonlyMap; + /** + * The times that the previous watcher had when the run started + */ + times: ReadonlyMap | undefined; +} + +/** + * The fields of watchpack's internal `DirectoryWatcher` that {@link refreshPendingTimes} reads. + * They aren't part of watchpack's public API, so they are all optional. + */ +interface IDirectoryWatcherInternals { + path?: string; + /** + * The names of the files that have an OS event whose `fs.lstat()` hasn't finished yet. The change is + * recorded only when the `fs.lstat()` finishes. + */ + _activeEvents?: Map; +} + +/** + * A watchpack instance with the internal state that {@link refreshPendingTimes} reads + */ +interface IWatchpackWithInternals extends Watchpack { + watcherManager?: { + directoryWatchers?: Map; + }; +} + +const OUTDATED_ON_ATTACH_EXPLANATION: string = 'watch (outdated on attach)'; +// A new watcher's first scan reports each file whose mtime is later than the watcher's start time, less the file +// system's accuracy. A watcher that attaches to the watcher of a folder that has already scanned reports the +// folder in the same way. Watchpack's other explanations are for events from the file system. +const SCAN_EXPLANATIONS: ReadonlySet = new Set(['scan (file)', OUTDATED_ON_ATTACH_EXPLANATION]); + +function tryLstatSync(filePath: string): fs.Stats | undefined { + try { + return fs.lstatSync(filePath, { throwIfNoEntry: false }); + } catch { + return undefined; + } +} + +function getTimestamp(stats: fs.Stats): number { + return stats.mtime.getTime() || stats.ctime.getTime() || Date.now(); +} + +/** + * Watchpack records a file's new time only when its `fs.lstat()` after the file system's event finishes. If a + * run starts before then, the previous watcher's time for the file is from before the change. Read the time + * again, so that the run sees the change, and so that the run's watcher knows that the run saw it. + */ +function refreshPendingTimes(watcher: Watchpack, times: Map): void { + const directoryWatchers: Map | undefined = ( + watcher as IWatchpackWithInternals + ).watcherManager?.directoryWatchers; + if (!(directoryWatchers instanceof Map)) { + return; + } + + for (const { path: folderPath, _activeEvents: pendingNames } of directoryWatchers.values()) { + if (typeof folderPath !== 'string' || !(pendingNames instanceof Map)) { + continue; + } + + for (const name of pendingNames.keys()) { + const filePath: string = path.join(folderPath, name); + const stats: fs.Stats | undefined = tryLstatSync(filePath); + if (!stats) { + times.delete(filePath); + } else if (!stats.isDirectory()) { + const timestamp: number = getTimestamp(stats); + times.set(filePath, { timestamp, safeTime: timestamp }); + } + } + } +} + +/** + * Returns false if the watcher reported the path only because the path's time is close to the run's start, and + * the run has already read the path as it is now. + */ +function isReportedChangeNew(filePath: string, explanation: string, inputs: IRunInputs): boolean { + if (!SCAN_EXPLANATIONS.has(explanation) || inputs.baseline === undefined) { + return true; + } + + const stats: fs.Stats | undefined = tryLstatSync(filePath); + if (!stats) { + return true; + } + + if (stats.isDirectory()) { + // The watcher of a folder that the run read reports the folder's files one by one + return explanation !== OUTDATED_ON_ATTACH_EXPLANATION || !inputs.contexts.has(filePath); + } + + if (stats.ctimeMs >= inputs.baseline) { + return true; + } + + const readTime: number | undefined = inputs.files.get(filePath) ?? inputs.times?.get(filePath)?.timestamp; + return readTime !== getTimestamp(stats); +} + /** * A filesystem adapter for use with the "fast-glob" package. This adapter tracks file system accesses * to initialize `watchpack`. @@ -261,11 +381,21 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { public setBaseline(): void { this.#lastQueryTime = Date.now(); - if (this.#watcher) { - const times: Map = new Map(); - this.#watcher.pause(); - this.#watcher.collectTimeInfoEntries(times, times); + const watcher: Watchpack | undefined = this.#watcher; + if (watcher) { + this.#watcher = undefined; + const times: Map = new Map(); + watcher.collectTimeInfoEntries(times, times); + refreshPendingTimes(watcher, times); + // Close the previous watcher instead of only pausing it. A paused watcher keeps its directory watchers, + // so every run would leak another set of them. A kept watcher also keeps its OS watch on a folder that + // was deleted and recreated, and later watchers on that folder would share the dead watch, so edits in + // the recreated folder would be reported one run late. + watcher.close(); this.#times = times; + } else { + // Nothing is watching, so times collected for an earlier run may be out of date. + this.#times = undefined; } } @@ -277,11 +407,39 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { return; } + const inputs: IRunInputs = { + baseline: this.#lastQueryTime, + files: this.#files, + contexts: this.#contexts, + times: this.#times + }; + const watcher: Watchpack = new Watchpack({ aggregateTimeout: 0, followSymlinks: false }); + // A file that changed just before the run started is reported again when the watcher starts, although the + // run has read the change. Call onChange only for a change that the run may not have seen. + let hasNewChange: boolean = false; + const onReportedChange = (filePath: string, modifiedTime: number, explanation: string): void => { + hasNewChange ||= isReportedChangeNew(filePath, explanation, inputs); + }; + const onReportedRemove = (): void => { + hasNewChange = true; + }; + const onAggregated = (): void => { + if (hasNewChange) { + watcher.off('change', onReportedChange); + watcher.off('remove', onReportedRemove); + watcher.off('aggregated', onAggregated); + onChange(); + } + }; + watcher.on('change', onReportedChange); + watcher.on('remove', onReportedRemove); + watcher.on('aggregated', onAggregated); + this.#watcher = watcher; watcher.watch({ files: this.#files.keys(), @@ -290,12 +448,11 @@ export class WatchFileSystemAdapter implements IWatchFileSystemAdapter { startTime: this.#lastQueryTime }); + // The watcher checks its reports against `inputs`, so the next run records into new maps this.#lastFiles = this.#files; this.#files = new Map(); - this.#contexts.clear(); + this.#contexts = new Map(); this.#missing.clear(); - - watcher.once('aggregated', onChange); } /** diff --git a/apps/heft/src/utilities/test/CliUtilities.test.ts b/apps/heft/src/utilities/test/CliUtilities.test.ts new file mode 100644 index 00000000000..ef2fcf633dc --- /dev/null +++ b/apps/heft/src/utilities/test/CliUtilities.test.ts @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { getErrorExitCode } from '../CliUtilities'; + +describe(getErrorExitCode.name, () => { + const cases: [string | number | undefined, number][] = [ + [undefined, 1], + [0, 1], + ['0', 1], + ['', 1], + [-1, 1], + [1.5, 1], + ['abc', 1], + [1, 1], + [2, 2], + ['3', 3] + ]; + + it.each(cases)( + 'maps an exit code of %p to %p', + (exitCode: string | number | undefined, expected: number) => { + expect(getErrorExitCode(exitCode)).toBe(expected); + } + ); +}); diff --git a/apps/heft/src/utilities/test/WatchFileSystemAdapter.test.ts b/apps/heft/src/utilities/test/WatchFileSystemAdapter.test.ts new file mode 100644 index 00000000000..287a8403e29 --- /dev/null +++ b/apps/heft/src/utilities/test/WatchFileSystemAdapter.test.ts @@ -0,0 +1,588 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; + +import Watchpack from 'watchpack'; + +import { Async } from '@rushstack/node-core-library'; + +import { watchGlobAsync } from '../../plugins/FileGlobSpecifier'; +import { type IWatchedFileState, type StatCallback, WatchFileSystemAdapter } from '../WatchFileSystemAdapter'; + +const RUN_REQUEST_TIMEOUT_MS: number = 5000; +// Long enough for a watcher to finish its first scan, and for events to arrive +const SETTLE_MS: number = 250; +const TEST_TIMEOUT_MS: number = 30000; +// An hour ago, plus some milliseconds so that watchpack sees the file system's timestamps are precise. Files get +// old mtimes so that a new watcher doesn't report a file as changed just because it was written right before +// the watcher started. +const BASE_MTIME_MS: number = Math.floor(Date.now() / 1000) * 1000 - 3600 * 1000 + 123; +// A new watcher reports a file whose mtime is later than the watcher's start time. A file that gets an mtime in +// the future is reported by the next run's watcher for certain. +const FUTURE_MTIME_OFFSET_MS: number = 60 * 1000; +// Long enough for file timestamps to show which of two events came first, even where they are coarse +const TIMESTAMP_GAP_MS: number = 20; +// A file that the glob doesn't match, in a folder that the glob reads +const NOTES_PATH: string = 'src/sub/notes.txt'; + +// Watchpack calls fs.lstat() through this graceful-fs instance +const watcherFs: typeof fs = jest.requireActual( + require.resolve('graceful-fs', { paths: [path.dirname(require.resolve('watchpack/package.json'))] }) +); +// The adapter's calls to fs.lstatSync() look up the function on this module when they are made +const nodeFs: typeof fs = jest.requireActual('node:fs'); + +interface IRunOptions { + watch?: boolean; + // The glob that the run reads. The default reads every folder under src. + pattern?: string; + beforeWatch?: () => Promise | void; +} + +interface IRun { + changed: string[]; + isRunRequested(): boolean; + waitForRunRequestAsync(): Promise; + /** + * Waits until the run's watcher has reported the path with the explanation, and has passed the report on + * in an 'aggregated' event. Then waits SETTLE_MS more, so that later events can arrive. Returns false if the + * watcher doesn't report the path. + */ + waitForReportAsync(relativePath: string, explanation: string): Promise; +} + +describe(WatchFileSystemAdapter.name, () => { + let rootFolder: string; + let writeCount: number; + let adapter: WatchFileSystemAdapter; + let watchSpy: jest.SpyInstance; + let collectSpy: jest.SpyInstance; + let closeSpy: jest.SpyInstance; + + function writeFile(relativePath: string, mtimeMs?: number): void { + const filePath: string = path.join(rootFolder, relativePath); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + writeCount++; + fs.writeFileSync(filePath, `export const value: number = ${writeCount};\n`); + const mtime: Date = new Date(mtimeMs ?? BASE_MTIME_MS + writeCount * 1000); + fs.utimesSync(filePath, mtime, mtime); + } + + // Drives the adapter the way TaskOperationRunner does in watch mode + async function runAsync(options: IRunOptions = {}): Promise { + const { watch = true, pattern = 'src/**/*.ts', beforeWatch } = options; + adapter.setBaseline(); + const states: Map = await watchGlobAsync(pattern, { + cwd: rootFolder, + fs: adapter + }); + const changed: string[] = []; + for (const [file, state] of states) { + if (state.changed) { + changed.push(file); + } + } + changed.sort(); + + await beforeWatch?.(); + + let requested: boolean = false; + let onRequest: (() => void) | undefined; + const watcherCount: number = watchSpy.mock.contexts.length; + if (watch) { + adapter.watch(() => { + requested = true; + onRequest?.(); + }); + } + + // The reports of the run's watcher, as of its last 'aggregated' event + const reports: Set = new Set(); + let onReports: (() => void) | undefined; + const watcher: Watchpack | undefined = watchSpy.mock.contexts[watcherCount]; + if (watcher) { + let newReports: string[] = []; + watcher.on('change', (filePath: string, modifiedTime: number, explanation: string) => { + newReports.push(`${explanation}: ${filePath}`); + }); + watcher.on('remove', (filePath: string, explanation: string) => { + newReports.push(`${explanation}: ${filePath}`); + }); + // Runs after the adapter's listener, which the adapter added first + watcher.on('aggregated', () => { + for (const report of newReports) { + reports.add(report); + } + newReports = []; + onReports?.(); + }); + } + + return { + changed, + isRunRequested: () => requested, + waitForRunRequestAsync: () => + new Promise((resolve: (value: boolean) => void) => { + if (requested) { + resolve(true); + return; + } + const timeout: NodeJS.Timeout = setTimeout(() => resolve(false), RUN_REQUEST_TIMEOUT_MS); + onRequest = () => { + clearTimeout(timeout); + resolve(true); + }; + }), + waitForReportAsync: async (relativePath: string, explanation: string): Promise => { + const report: string = `${explanation}: ${path.join(rootFolder, relativePath)}`; + const isReported: boolean = await new Promise((resolve: (value: boolean) => void) => { + const timeout: NodeJS.Timeout = setTimeout(() => resolve(false), RUN_REQUEST_TIMEOUT_MS); + onReports = () => { + if (reports.has(report)) { + clearTimeout(timeout); + resolve(true); + } + }; + onReports(); + }); + await Async.sleepAsync(SETTLE_MS); + return isReported; + } + }; + } + + // The first run's watcher may request a run just because the folders are new, so tests only use it to start + async function startAsync(): Promise { + writeFile('src/a.ts'); + writeFile('src/sub/b.ts'); + const run: IRun = await runAsync(); + expect(run.changed).toEqual(['src/a.ts', 'src/sub/b.ts']); + await Async.sleepAsync(SETTLE_MS); + } + + // Also writes the file at NOTES_PATH. Returns the second run, whose watcher has scanned the files without + // requesting a run. The next run collects that watcher's times. + async function startWatchingAsync(): Promise { + writeFile(NOTES_PATH); + await startAsync(); + const run2: IRun = await runAsync(); + expect(run2.changed).toEqual([]); + await Async.sleepAsync(SETTLE_MS); + expect(run2.isRunRequested()).toBe(false); + return run2; + } + + // Gives the file a future mtime, and waits until the run's watcher has recorded the change. Returns the mtime. + async function changeBeforeNextRunAsync(run: IRun, relativePath: string): Promise { + const mtimeMs: number = Date.now() + FUTURE_MTIME_OFFSET_MS; + writeFile(relativePath, mtimeMs); + expect(await run.waitForRunRequestAsync()).toBe(true); + await Async.sleepAsync(SETTLE_MS); + return mtimeMs; + } + + // From now on, the adapter sees a ctime for the file that is earlier than the run's start. A file that changed + // after the run started can have such a ctime where timestamps are coarse, or where the clock that sets them + // lags Date.now(). + function backdateCtime(relativePath: string): void { + const backdatedPath: string = path.join(rootFolder, relativePath); + const lstatSync: typeof fs.lstatSync = nodeFs.lstatSync; + jest.spyOn(nodeFs, 'lstatSync').mockImplementation((( + filePath: fs.PathLike, + options?: fs.StatSyncOptions + ) => { + const stats: fs.Stats | undefined = lstatSync(filePath, options) as fs.Stats | undefined; + if (stats && filePath === backdatedPath) { + stats.ctimeMs = BASE_MTIME_MS; + } + return stats; + }) as unknown as typeof fs.lstatSync); + } + + // Replaces watchpack's fs.lstat(). The handler calls lstatAsync() to do the fs.lstat() and give watchpack the + // result. + function interceptWatcherLstat(handler: (filePath: string, lstatAsync: () => Promise) => void): void { + const lstat: (filePath: string, callback: StatCallback) => void = watcherFs.lstat; + jest.spyOn(watcherFs, 'lstat').mockImplementation(((filePath: string, callback: StatCallback) => { + handler(filePath, async () => { + await new Promise((resolve: () => void) => { + lstat(filePath, (error, stats) => { + callback(error, stats); + resolve(); + }); + }); + }); + }) as unknown as typeof watcherFs.lstat); + } + + // Holds watchpack's next fs.lstat() of the file, as a slow file system can. Resolves when watchpack calls it, + // with a function that finishes it. + function holdNextWatcherLstatAsync(relativePath: string): Promise<(() => void) | undefined> { + const heldPath: string = path.join(rootFolder, relativePath); + return new Promise((resolve: (finish: (() => void) | undefined) => void) => { + const timeout: NodeJS.Timeout = setTimeout(() => resolve(undefined), RUN_REQUEST_TIMEOUT_MS); + let isHolding: boolean = false; + interceptWatcherLstat((filePath: string, lstatAsync: () => Promise) => { + if (filePath === heldPath && !isHolding) { + isHolding = true; + clearTimeout(timeout); + resolve(() => { + void lstatAsync(); + }); + } else { + void lstatAsync(); + } + }); + }); + } + + // Holds watchpack's fs.lstat() of the folder until watchpack has finished its fs.lstat() of each of the files + function holdWatcherFolderLstat(relativeFolderPath: string, fileNames: string[]): void { + const folderPath: string = path.join(rootFolder, relativeFolderPath); + const unreadFiles: Set = new Set(fileNames.map((name: string) => path.join(folderPath, name))); + let finishFolderLstat: (() => Promise) | undefined; + const finishWhenFilesAreRead = (): void => { + if (finishFolderLstat && unreadFiles.size === 0) { + void finishFolderLstat(); + finishFolderLstat = undefined; + } + }; + interceptWatcherLstat((filePath: string, lstatAsync: () => Promise) => { + if (filePath === folderPath) { + finishFolderLstat = lstatAsync; + finishWhenFilesAreRead(); + } else { + void lstatAsync().then(() => { + unreadFiles.delete(filePath); + finishWhenFilesAreRead(); + }); + } + }); + } + + beforeEach(() => { + rootFolder = fs.mkdtempSync(path.join(os.tmpdir(), 'heft-watch-fs-')); + writeCount = 0; + watchSpy = jest.spyOn(Watchpack.prototype, 'watch'); + collectSpy = jest.spyOn(Watchpack.prototype, 'collectTimeInfoEntries'); + closeSpy = jest.spyOn(Watchpack.prototype, 'close'); + adapter = new WatchFileSystemAdapter(); + }); + + afterEach(() => { + for (const watcher of watchSpy.mock.contexts) { + (watcher as Watchpack).close(); + } + jest.restoreAllMocks(); + fs.rmSync(rootFolder, { recursive: true, force: true }); + }); + + it( + 'requests a run when a watched file changes, and reports the file in that run', + async () => { + await startAsync(); + + const run2: IRun = await runAsync(); + expect(run2.changed).toEqual([]); + await Async.sleepAsync(SETTLE_MS); + expect(run2.isRunRequested()).toBe(false); + + writeFile('src/sub/b.ts'); + expect(await run2.waitForRunRequestAsync()).toBe(true); + const run3: IRun = await runAsync(); + expect(run3.changed).toEqual(['src/sub/b.ts']); + }, + TEST_TIMEOUT_MS + ); + + it( + 'requests another run for a file that changes while a run is in progress', + async () => { + await startAsync(); + + // The file changes after the glob has read it, and before the run starts watching + const run2: IRun = await runAsync({ + beforeWatch: () => writeFile('src/sub/b.ts', Date.now()) + }); + expect(run2.changed).toEqual([]); + expect(await run2.waitForRunRequestAsync()).toBe(true); + const run3: IRun = await runAsync(); + expect(run3.changed).toEqual(['src/sub/b.ts']); + }, + TEST_TIMEOUT_MS + ); + + it( + 'closes the previous watcher, after collecting its times, when the next run starts', + async () => { + writeFile('src/a.ts'); + await runAsync(); + expect(watchSpy).toHaveBeenCalledTimes(1); + const watcher1: Watchpack = watchSpy.mock.contexts[0]; + expect(closeSpy).not.toHaveBeenCalled(); + + await runAsync(); + expect(watchSpy).toHaveBeenCalledTimes(2); + const watcher2: Watchpack = watchSpy.mock.contexts[1]; + expect(watcher2).not.toBe(watcher1); + expect(collectSpy).toHaveBeenCalledTimes(1); + expect(collectSpy.mock.contexts[0]).toBe(watcher1); + expect(closeSpy).toHaveBeenCalledTimes(1); + expect(closeSpy.mock.contexts[0]).toBe(watcher1); + expect(collectSpy.mock.invocationCallOrder[0]).toBeLessThan(closeSpy.mock.invocationCallOrder[0]); + + await runAsync(); + expect(closeSpy).toHaveBeenCalledTimes(2); + expect(closeSpy.mock.contexts[1]).toBe(watcher2); + }, + TEST_TIMEOUT_MS + ); + + it( + 'reports a file that changed after a run that did not start watching', + async () => { + await startAsync(); + + // Like a run in which copying files fails, so that TaskOperationRunner never calls watch() + const run2: IRun = await runAsync({ watch: false }); + expect(run2.changed).toEqual([]); + + writeFile('src/sub/b.ts'); + await Async.sleepAsync(SETTLE_MS); + const run3: IRun = await runAsync(); + expect(run3.changed).toEqual(['src/sub/b.ts']); + }, + TEST_TIMEOUT_MS + ); + + // On Linux, a watch stays on the deleted folder. Windows doesn't let a watched folder be recreated. + (process.platform === 'linux' ? it : it.skip)( + 'reports a change in a folder that was deleted and recreated, in the run after the change', + async () => { + await startAsync(); + + const run2: IRun = await runAsync(); + expect(run2.changed).toEqual([]); + await Async.sleepAsync(SETTLE_MS); + + // Delete and recreate the folder, as switching branches can + fs.rmSync(path.join(rootFolder, 'src/sub'), { recursive: true }); + writeFile('src/sub/b.ts'); + expect(await run2.waitForRunRequestAsync()).toBe(true); + await Async.sleepAsync(SETTLE_MS); + + const run3: IRun = await runAsync(); + expect(run3.changed).toEqual(['src/sub/b.ts']); + await Async.sleepAsync(SETTLE_MS); + expect(run3.isRunRequested()).toBe(false); + + writeFile('src/sub/b.ts'); + expect(await run3.waitForRunRequestAsync()).toBe(true); + const run4: IRun = await runAsync(); + expect(run4.changed).toEqual(['src/sub/b.ts']); + await Async.sleepAsync(SETTLE_MS); + + const run5: IRun = await runAsync(); + expect(run5.changed).toEqual([]); + }, + TEST_TIMEOUT_MS + ); + + it( + 'does not request a run for a file that changed before the run, but does for a later change', + async () => { + const run2: IRun = await startWatchingAsync(); + await changeBeforeNextRunAsync(run2, NOTES_PATH); + + // The new watcher reports the file, because of its mtime. The glob doesn't match it, but the previous + // watcher's times show that it hasn't changed since the run started. + const run3: IRun = await runAsync(); + expect(run3.changed).toEqual([]); + expect(await run3.waitForReportAsync(NOTES_PATH, 'scan (file)')).toBe(true); + expect(run3.isRunRequested()).toBe(false); + + writeFile('src/sub/b.ts'); + expect(await run3.waitForRunRequestAsync()).toBe(true); + const run4: IRun = await runAsync(); + expect(run4.changed).toEqual(['src/sub/b.ts']); + }, + TEST_TIMEOUT_MS + ); + + it( + 'does not request another run for a watched file that changed before the run started', + async () => { + const run2: IRun = await startWatchingAsync(); + await changeBeforeNextRunAsync(run2, 'src/sub/b.ts'); + + const run3: IRun = await runAsync(); + expect(run3.changed).toEqual(['src/sub/b.ts']); + expect(await run3.waitForReportAsync('src/sub/b.ts', 'scan (file)')).toBe(true); + expect(run3.isRunRequested()).toBe(false); + }, + TEST_TIMEOUT_MS + ); + + it( + 'does not request another run for a file that the first run read', + async () => { + writeFile('src/b.ts', Date.now() + FUTURE_MTIME_OFFSET_MS); + await Async.sleepAsync(TIMESTAMP_GAP_MS); + + // There is no previous watcher, so only the time that the run read is known + const run1: IRun = await runAsync(); + expect(run1.changed).toEqual(['src/b.ts']); + expect(await run1.waitForReportAsync('src/b.ts', 'scan (file)')).toBe(true); + expect(run1.isRunRequested()).toBe(false); + }, + TEST_TIMEOUT_MS + ); + + it( + 'requests a run for a file that is written again during the run, with the same mtime', + async () => { + const run2: IRun = await startWatchingAsync(); + const mtimeMs: number = await changeBeforeNextRunAsync(run2, NOTES_PATH); + + const run3: IRun = await runAsync({ + // The wait makes the file's ctime later than the run's start + beforeWatch: () => Async.sleepAsync(TIMESTAMP_GAP_MS).then(() => writeFile(NOTES_PATH, mtimeMs)) + }); + expect(run3.changed).toEqual([]); + expect(await run3.waitForRunRequestAsync()).toBe(true); + }, + TEST_TIMEOUT_MS + ); + + it( + 'requests a run for a watched file whose mtime moves back after the run read it', + async () => { + const run2: IRun = await startWatchingAsync(); + const mtimeMs: number = await changeBeforeNextRunAsync(run2, 'src/sub/b.ts'); + + const run3: IRun = await runAsync({ + beforeWatch: () => { + // Earlier than the mtime that the run read, but later than the watcher's start, so that the watcher + // reports the file + writeFile('src/sub/b.ts', mtimeMs - FUTURE_MTIME_OFFSET_MS / 2); + backdateCtime('src/sub/b.ts'); + } + }); + expect(run3.changed).toEqual(['src/sub/b.ts']); + expect(await run3.waitForReportAsync('src/sub/b.ts', 'scan (file)')).toBe(true); + expect(run3.isRunRequested()).toBe(true); + }, + TEST_TIMEOUT_MS + ); + + it( + 'does not request a run for a file whose change the previous watcher had not finished reading', + async () => { + await startWatchingAsync(); + const lstatHeld: Promise<(() => void) | undefined> = holdNextWatcherLstatAsync(NOTES_PATH); + writeFile(NOTES_PATH, Date.now() + FUTURE_MTIME_OFFSET_MS); + const finishLstat: (() => void) | undefined = await lstatHeld; + expect(finishLstat).toBeDefined(); + await Async.sleepAsync(TIMESTAMP_GAP_MS); + + // The run starts while the previous watcher still has the file's old time + const run3: IRun = await runAsync(); + finishLstat?.(); + expect(run3.changed).toEqual([]); + expect(await run3.waitForReportAsync(NOTES_PATH, 'scan (file)')).toBe(true); + expect(run3.isRunRequested()).toBe(false); + }, + TEST_TIMEOUT_MS + ); + + it( + 'reports a watched file whose change the previous watcher had not finished reading, in that run', + async () => { + await startWatchingAsync(); + const lstatHeld: Promise<(() => void) | undefined> = holdNextWatcherLstatAsync('src/sub/b.ts'); + writeFile('src/sub/b.ts', Date.now() + FUTURE_MTIME_OFFSET_MS); + const finishLstat: (() => void) | undefined = await lstatHeld; + expect(finishLstat).toBeDefined(); + await Async.sleepAsync(TIMESTAMP_GAP_MS); + + const run3: IRun = await runAsync(); + finishLstat?.(); + expect(run3.changed).toEqual(['src/sub/b.ts']); + expect(await run3.waitForReportAsync('src/sub/b.ts', 'scan (file)')).toBe(true); + expect(run3.isRunRequested()).toBe(false); + }, + TEST_TIMEOUT_MS + ); + + it( + 'does not request a run when a folder that the run read is reported as its watcher is attached', + async () => { + const run2: IRun = await startWatchingAsync(); + await changeBeforeNextRunAsync(run2, NOTES_PATH); + + // The watcher of src attaches to the watcher of src/sub when its fs.lstat() of src/sub finishes. If + // src/sub's watcher has read a file with a new time by then, the watcher reports src/sub. + const run3: IRun = await runAsync({ + beforeWatch: () => holdWatcherFolderLstat('src/sub', ['b.ts', 'notes.txt']) + }); + expect(run3.changed).toEqual([]); + expect(await run3.waitForReportAsync('src/sub', 'watch (outdated on attach)')).toBe(true); + expect(run3.isRunRequested()).toBe(false); + }, + TEST_TIMEOUT_MS + ); + + it( + 'requests a run when a folder that the run did not read is reported as its watcher is attached', + async () => { + const run2: IRun = await startWatchingAsync(); + await changeBeforeNextRunAsync(run2, NOTES_PATH); + + // The run reads src and src/sub/b.ts, but not src/sub, so the watcher doesn't report the files in src/sub + // one by one. The watcher of src attaches to the watcher that src/sub has for b.ts, and reports src/sub. + const run3: IRun = await runAsync({ + pattern: 'src/*.ts', + beforeWatch: () => { + adapter.getStateAndTrack(path.join(rootFolder, 'src/sub/b.ts')); + holdWatcherFolderLstat('src/sub', ['b.ts', 'notes.txt']); + } + }); + expect(run3.changed).toEqual([]); + expect(await run3.waitForReportAsync('src/sub', 'watch (outdated on attach)')).toBe(true); + expect(run3.isRunRequested()).toBe(true); + }, + TEST_TIMEOUT_MS + ); + + it( + 'requests a run for a file system event, even if the file looks unchanged', + async () => { + const run2: IRun = await startWatchingAsync(); + const mtimeMs: number = await changeBeforeNextRunAsync(run2, NOTES_PATH); + const run3: IRun = await runAsync(); + expect(await run3.waitForReportAsync(NOTES_PATH, 'scan (file)')).toBe(true); + + backdateCtime(NOTES_PATH); + writeFile(NOTES_PATH, mtimeMs); + expect(await run3.waitForRunRequestAsync()).toBe(true); + }, + TEST_TIMEOUT_MS + ); + + it( + 'requests a run for a watched file that is deleted during the run', + async () => { + await startWatchingAsync(); + + const run3: IRun = await runAsync({ + beforeWatch: () => fs.unlinkSync(path.join(rootFolder, 'src/sub/b.ts')) + }); + expect(run3.changed).toEqual([]); + expect(await run3.waitForRunRequestAsync()).toBe(true); + }, + TEST_TIMEOUT_MS + ); +}); diff --git a/apps/rush-cli-client/README.md b/apps/rush-cli-client/README.md index 48ea3a20ab5..ab9551ac87a 100644 --- a/apps/rush-cli-client/README.md +++ b/apps/rush-cli-client/README.md @@ -37,11 +37,73 @@ reporter behavior without duplicating or relocating bootstrap code. Routing precedence: -1. `--no-daemon` before `--`, help, never-daemonize commands, and Rushx with any TTY stdio stay in-process. +1. `--no-daemon` before `--`, help, never-daemonize commands, an option before the command other than + `--quiet`/`-q` (such as `--debug`), and Rushx with any TTY stdio stay in-process. `--quiet` and `-q` only hide + native Rush's startup banner, which a daemon request never prints, so `rush -q build` is sent to the daemon + without them. 2. CI stays in-process unless `RUSH_DAEMON=1` explicitly opts in, even if config enables the daemon. 3. `RUSH_DAEMON` overrides `rush.json`'s `daemon.enabled`; the default is false. 4. Auto-start is considered only after selecting daemon execution. +A command that routing keeps in-process shows no progress line. It says why in one stderr line before native +Rush starts, in agent mode (see [Output modes](#output-modes)) and, in legacy mode, when `RUSH_DAEMON=1` asked +for the daemon. Rushx uses the `rushx-client:` prefix. `--no-daemon` and help print nothing. The lines are: + +- `rush-client: RUSH_LOG_LEVEL selects the native reporter; using in-process Rush.` The same line names + `RUSH_REPORTER=`, `--reporter`, `--output`, `--log-level` or `useRushReporter in experiments.json` + (see below). +- `rush-client: the daemon does not support "--debug"; using in-process Rush.` +- `rush-client: the daemon does not run "check"; using in-process Rush.` +- `rushx-client: the daemon does not run scripts in a terminal; using in-process Rush.` +- `rush-client: RUSH_DAEMON=0 turns the daemon off; using in-process Rush.` +- `rush-client: the daemon is not enabled for this repo; using in-process Rush.` To enable it, set `daemon.enabled` + to `true` in `rush.json`, or set `RUSH_DAEMON=1` in the environment. +- `rush-client: CI is set, so the daemon is off unless RUSH_DAEMON=1; using in-process Rush.` The line names + the first CI marker that is set: `CI`, `TF_BUILD`, `GITHUB_ACTIONS`, `JENKINS_URL` or `TEAMCITY_VERSION`. + +When the selected daemon cannot be reached or started, an ordinary invocation prints the reason and runs +in-process (`rush-client: ; using in-process Rush.`). A startup failure while a live process can +still make the daemon ready is the exception: a process that listens at the endpoint but does not +complete hello/ping in time, a startup helper that still waits for its daemon, or another client that +holds the start mutex, as in a burst of clients that all find no daemon. In-process Rush would take the +repository lock, and the requests that the daemon serves would then wait for it, or fail when their wait +timeout ends (see below). Instead, the client keeps trying for one more startup deadline +(15 seconds, so about 30 seconds in all) and uses the daemon once it is ready. It says so when it starts +waiting (`rush-client: The daemon is not ready yet. , so this command waits up to 15 s more +for it instead of running Rush in-process.`; agent output shows "rushd is still starting; waiting for it" +as the progress phase, and on a pipe writes it as a progress line that ends with `because `). If the daemon is still not ready, the command exits with code 1. The message +gives the startup error with its `--no-daemon` hint, then the process that is still live, "so Rush was +not run in-process", and a pointer to `rush-client daemon status`. When the process that the daemon's +ownership record names still runs but does not answer (on Linux, for example because a signal stopped +it), the message instead says what that process is doing, then on a line of its own that Rush was not +run in-process, and its last line says what to do, for example `Resume it with "kill -CONT "; it +then serves the next command.` On Linux, when that process has this workspace's ownership record open, as the daemon that +wrote it does, and stays stopped (state T or t) while the client samples it for 1.5 s, the command does +not wait for either deadline: it exits with code 1 and that message once the 1.5 s have passed. It names +a signal to send only to a Rush daemon that has that record open; for any other process it says to end +that process if it is this workspace's daemon, and else to delete the ownership record, and that until +then each command that uses the daemon first waits 15 s for a daemon to answer. Such a Rush +daemon that still runs after its socket file was deleted also fails the command this way, with code 1 +and without running Rush in-process, once the client has waited 15 seconds for it to exit; its last line +says that it may exit once its running requests finish. When the process that the ownership record names +has exited but is not reaped yet (on Linux, state Z), nothing live can make the daemon ready, so the +command runs Rush in-process once its startup deadline (15 s) has passed. The last line of its message +names the parent that has not reaped that process, and says that until then each command that uses the +daemon first waits 15 s for a daemon to answer. + +When the client runs Rush in-process after it tried the daemon, because it could not reach one or because the +daemon handed the request back, and another Rush process holds the repository's lock, such as the daemon while it +builds for another request, Rush waits for the lock instead of failing at once with "Another Rush command is +already running in this repository." It waits only for what is left of the request's wait timeout (see below), +counted from when the client sent the request, or from when it gave up on the daemon if it could not reach one; +the built-in 30-second default applies. It writes one stderr line when it starts to wait (`Waiting up to 28 s for +the Rush daemon (PID 4242) to release this repository's lock.`). If the lock is still held at the deadline, the +command fails as before, and the error names the holder (`The Rush daemon (PID 4242) still holds this repository's +lock.`). With `--no-wait` or a zero timeout, Rush tries once and names the holder. On Windows the lock file does not +name a process, so Rush says "another Rush process". Rush that routing keeps in-process (such as `--no-daemon`, +`RUSH_DAEMON=0` or CI), `rushx-client`, and a workspace that selects another Rush release than the one the client +bundles fail at once, as native Rush does. + `--no-wait` fails immediately when daemon admission is unavailable. `--wait-timeout SECONDS` (or `--wait-timeout=SECONDS`) overrides the configured queue timeout; finite nonnegative decimal seconds up to 2147483.647 are accepted and @@ -49,24 +111,177 @@ rounded down to milliseconds. These controls are mutually exclusive and are consumed before forwarding, never appended to a project script. Arguments after `--` remain literal script arguments. -The queue timeout is measured from when the daemon receives the request. An -explicit `--no-wait`, `--wait-timeout`, `RUSH_DAEMON_QUEUE_TIMEOUT_SECONDS`, or -`daemon.queueTimeoutSeconds` in `rush.json` bounds the entire wait: waiting for -workspace admission and waiting for a running build that the request could not -join. The built-in 30-second default bounds only workspace admission (for example, -waiting for a command that needs exclusive access). With the default, a build that -arrives while a compatible build is already running waits for it to finish and then -runs, instead of failing after 30 seconds. On a timeout, the client exits with -code 1 and says how to wait longer. +Only time that the request spends waiting for other requests counts against the +queue timeout, whether it comes from `--wait-timeout`, +`RUSH_DAEMON_QUEUE_TIMEOUT_SECONDS`, `daemon.queueTimeoutSeconds` in `rush.json`, +or the built-in 30-second default. Waiting while another request +loads or reloads the workspace graph does not count, so every build that arrives +while the first build after startup loads the graph runs once the load finishes. +That wait fails after 10 times the timeout (5 minutes with the default), so a load +that never finishes does not hold other requests forever. The request's own work, +such as checking its inputs, loading the graph, routing and execution, does not +count either. A configured or per-invocation timeout also +limits waiting for a running build to start so that the request can join it (with +`joinRunningBatch`), waiting for a running build that the request could not join, and waiting for +the requests that the daemon is serving to finish before it restarts for the +request's environment. The built-in default does not: with it, a build that arrives +while a compatible build is already running waits for it to finish and then runs, +instead of failing after 30 seconds, and a request that needs a restart waits for +the requests that were running when it arrived to finish and then runs on the +restarted daemon. The default still limits a restart wait while the daemon runs a +`rushx` script, such as a dev server, which may not exit until it is stopped, and +while it serves requests that arrived later. A `rushx-client` script that arrives +while another request waits for the daemon to restart does not start on the old +daemon, where the restart would wait for it to exit: it waits for the restart and +then runs on the restarted daemon, and its timeout applies to that wait as it does +to the restart wait. `--no-wait` fails wherever the request would wait. On a +timeout, the client exits with code 1 and suggests `--wait-timeout`. It does not +suggest exporting `RUSH_DAEMON_QUEUE_TIMEOUT_SECONDS`, because Rush versions that do +not recognize a `RUSH_` environment variable fail every command while it is set. +In legacy output and in `rushx-client`, the admission failure line +(`rush-client: daemon admission failed (wait-timeout): …`, or `(no-wait)`; in +`rushx-client` it begins with `rushx-client:`) gives the daemon's reason, as agent +mode's summary line does, so it names what the request waited for, such as a daemon +restart, and why the daemon restarts. + +A request also waits while a Rush process that the daemon does not run, such as +`rush install` or a `--no-daemon` build, holds the repository's lock. Every timeout, +the built-in default included, limits that wait, since that process can run for any +length of time. Stderr, on a terminal and on a pipe, names the process at once +(`rush-client: waiting for another Rush process (PID 12345: rush install) to release +this repository's lock.`), and again with the time waited every 10 seconds +(`still waiting after 10s for …`); agent output shows it as the progress phase, and if the +daemon then restarts, `request resubmitted to the new daemon; preparing the workspace graph` +until the new daemon reports a queue position or starts the command. Only +Linux tells the PID and command; elsewhere the line says `another Rush process`. On +Linux, the name also says when that process is stopped, since it cannot release the +lock until something resumes it (`another Rush process (PID 12345: rush install; it is +stopped (state T), for example by SIGSTOP)`). +`--no-wait` and `--wait-timeout 0` fail at once, and the admission failure line of +these and of a timeout names the process. A request never waits for a lock that the +daemon itself holds for another request: it fails at once, as before. Admission controls also apply to experimental graph requests, but not -`start|stop|restart|status|logs`. They affect daemon admission only; native fallback +`start|stop|restart|status|logs`. They affect daemon admission, and how long Rush that runs in-process after +the client tried the daemon waits for the repository's lock (see above); otherwise native fallback retains native command behavior. Waiting positions are shown on interactive stderr, -and admission failures report their typed reason and a nonzero exit code. - -Explicit reporter/output/log-level controls retain the native frontend reporter path. -The current daemon client renders the legacy operation stream; it does not silently -reinterpret requests for JSON, AI, file, or other reporter formats. +and a wait for a daemon restart (see below) or for another Rush process on a pipe +too. Admission failures report their typed reason and a nonzero exit code. + +Explicit reporter/output/log-level controls (`--reporter`, `--output`, `--log-level`, +`RUSH_REPORTER` other than `legacy`, or `RUSH_LOG_LEVEL`) retain the native frontend +reporter path, with or without `--no-daemon`, including `--reporter=ai`. The daemon client +does not silently reinterpret requests for JSON, AI, file, or other reporter formats. + +A repository that opts into the native reporter with `"useRushReporter": true` in +`common/config/rush/experiments.json` also stays on the native (in-process) path, so +that its reporter output is honored rather than silently replaced by the daemon +stream. Native reporter rendering over the daemon protocol is a follow-up. + +### Output modes + +The `rush-client` daemon path has two output modes (`rushx-client` always uses `legacy`). +Requests that use the native reporter path (see above) always get native output, and +agent mode writes nothing ahead of it. Otherwise, selection precedence is: + +1. `RUSHD_OUTPUT=agent` or `RUSHD_OUTPUT=legacy`. +2. An active `COPILOT_CLI` agent marker selects `agent`, matching `detectAgent()` in + `@rushstack/reporter` (a value is inactive when empty, `0`, `false`, `no` or `off`). + Other agents can opt in with `RUSHD_OUTPUT=agent`. +3. Otherwise `legacy`: the unchanged collated operation stream. + +Agent mode is plain text for humans and agents, not the AI reporter's JSON record format; +use `--reporter=ai` for machine-parsed records. On a TTY it paints at most three live rows, the +first before `@microsoft/rush-lib` is loaded. On a pipe it writes one progress line when the +daemon has the request (`rush build · 0.1s · sent to rushd; preparing the workspace graph +(status at least every 25s)`), however many operations run. A longer request also gets status +lines, so that it does not look hung: one whenever nothing was written for 25 s, with the counts +and the running operations, and one when connecting to the daemon takes more than 10 s. A +wait for a daemon that is still starting gets a line of its own +(`rushd is still starting; waiting for it (up to 15s more) because its startup helper (PID 4242) is +still waiting for the daemon`). A +request that waited for admission says so at the end of its summary line +(`· queued behind another request (position 1 at 0.2s)`), unless it failed: a failure's summary +line gives the failure, not the wait. It always ends with one summary +line, for example +`rush build: SUCCESS 772/772 operations (12 success, 760 from cache) in 3.1s`, or +`up to date (no operations needed)`, or, when the selection parameters matched no projects, +`rush build: SUCCESS 0 operations in 0.5s · the selection parameters did not match any projects`. +The counts follow the native summary: silent operations +(such as phases a project does not define) are not counted unless they fail, and operations +that did not need to run (`SKIPPED` or `NO OP` in native output) are counted as `up to date`. The verdict is +`SUCCESS`, `FAILURE` or `CANCELLED` (Ctrl+C or a termination signal); a request that a daemon +shutdown aborted is a `FAILURE` whose summary line gives the reason, with exit code 1. When the +daemon did not admit the request in time, or at once with `--no-wait`, the reason on the summary +line starts with `daemon admission failed (wait-timeout)` or `daemon admission failed (no-wait)`, +as in legacy output, followed by the daemon's reason in full. Warnings and errors that Rush or a +Rush plugin writes outside any operation (for example a plugin that continues without the cloud +build cache) are written at the end, at most three lines of them, before the summary line and any +operations reported with it. + +A failed operation is reported as soon as it fails, while the rest of the request runs on: a +`failed: · full log: ` line and a short excerpt of its output, error lines +with the line that follows them first, then the last lines. Stack frames, `Require stack:` lists +and progress noise are left out, and so are a message that a tool repeats in its summary, an +error count that the shown errors account for, and, when the first error shown names a source +location, the lines before it. Up to three operations are reported. Two kinds are reported just +before the summary line instead: a failed operation that wrote no output, with the error from the +daemon's result, and, when no operation failed, the operations whose warnings failed the request +(`warnings: …`). An operation that succeeded and then got warnings, because its build cache entry +could not be written, is shown with the output that it wrote after it succeeded. The error of a +reported operation that wrote output is printed too, unless its excerpt shows it or it only gives +the exit code (`Returned error code: 1`): for example an error +thrown while the operation's build cache entry was restored. Only the daemon's result carries it, +so for an operation reported as it failed it comes just before the summary line, as an +`error: ` line followed by the error. On a pipe, a status line names a failed +operation that wrote no output 1 s after it failed, unless the result came first. The summary +line names up to five failed (or warning) operations. Every operation's full output is in its +project's `rush-logs/` folder, whether or not it was printed. +When the daemon ran an operation's incremental command (its `:incremental` script; see +`incrementalBuilds` below) and that command failed, the line reads +`failed: · incremental command; its next run uses the initial command · full log: `. +That command can fail where the initial command, which `--no-daemon` runs, would not. Warnings +that an incremental command reported get `· incremental command` in the same place. +When a request falls back to in-process Rush, agent mode stops and native output follows. + +In agent mode a failed `rush build` doesn't wait for all of its work. Its result comes once an +operation failed and none of the selected projects that no other selected project depends on (for +example, the projects named by `--to`) is still waiting or running. The daemon keeps running the +operations that the failure didn't block, so that the next build finds them done, and the summary +line counts them and names up to three, in name order +(`· 2 independent operations continue in rushd: lib-b (build), lib-c (build)`). A later `rush build` +waits for them. While it waits only for them, its output says so and names up to three of them. +In agent mode the phase reads +`queued behind 2 operations left running by an earlier failed command (position 1): lib-b (build), lib-c (build)`, +a status line on a pipe reads +`waiting for 2 operations left running by an earlier failed command (queue position 1 at 0.1s): lib-b (build), lib-c (build)`, +and the summary line ends with +`· queued behind 2 operations left running by an earlier failed command (position 1 at 0.1s): lib-b (build), lib-c (build)`. +Legacy output on a terminal prints +`rush-client: waiting for daemon admission (position 1) behind 2 operations left running by an earlier failed command: lib-b (build), lib-c (build).` +The daemon reports the position again each time one of them ends, so the phase and the status +line name only the ones that still run; the summary line keeps the first position that named them. +`rush rebuild`, `rush install` and `rush update`, a restart of the daemon for another +environment, and `rush-client daemon stop` stop them instead. So does a command that the daemon +doesn't run (such as a custom command that it can't serve), before the client runs it in-process; +the client then prints, indented under its fallback line, +`rushd stopped 2 operations left running by an earlier failed command (lib-b (build), lib-c (build)), so that this command can run in-process.` +A served command that makes the daemon reload its graph, such as `rush test` after `rush build`, +stops them as well, and so does a served phased command that isn't incremental, such as a custom +`rush retest`. These commands and a served `rush rebuild` name them while the daemon stops them: +the agent phase reads +`stopping 2 operations left running by an earlier failed command (position 1): lib-b (build), lib-c (build)`, +a status line on a pipe reads +`waiting while rushd stops 2 operations left running by an earlier failed command (queue position 1 at 0.1s): lib-b (build), lib-c (build)`, +and if the command succeeds or is cancelled, its summary line ends with +`· stopped 2 operations left running by an earlier failed command (position 1 at 0.1s): lib-b (build), lib-c (build)`. +Legacy output on a terminal prints +`rush-client: waiting for daemon admission (position 1) while rushd stops 2 operations left running by an earlier failed command: lib-b (build), lib-c (build).` +Older clients say that such a command is queued behind them. Older daemons don't name them, so the +summary line ends with `· queued behind another request (position 1 at 0.1s)`. +Rushx scripts, and built-in commands that only read the workspace (such as `rush list`), run +in-process alongside them, like two Rush commands at once in one checkout. +Older daemons report the failure when all of the work has ended. Positively identified built-in `install` and `update` follow the same opt-in routing precedence as workspace builds and require protocol **0.10** @@ -79,8 +294,9 @@ Request cwd, environment, argv, width and color are captured before connecting. The protocol currently expresses request color as a boolean; subscriptions carry the corresponding color level. There is no SIGWINCH forwarding. -The standalone host now binds native `build`/`rebuild` requests to a reusable -all-project graph. Native Rush parsing, project selection, graph plugins, and +The standalone host now binds native `build`/`rebuild` requests, and the phased +commands of command-line.json (such as `test`), to a reusable all-project graph. +Global commands still run in-process. Native Rush parsing, project selection, graph plugins, and incremental/cache semantics are reused rather than spawning another Rush CLI. The client renders operation headers, collated text, and activity events; global command byte streams remain byte-preserving. A `rushx build` script never claims @@ -139,9 +355,10 @@ coalesced iteration, not while idle; native commands and `--no-daemon` can run after a completed request without stopping the daemon. Native workspace dispatch copies the request envelope and normalizes only the -engine-owned `_RUSH_LIB_PATH` to this daemon's real engine. Foreign client SDK -paths therefore neither select the wrong SDK nor cause a false restart. All other -environment inputs remain unchanged and participate in normal lifecycle checks. +engine-owned `_RUSH_LIB_PATH` to this daemon's own engine, keeping the spelling +that the engine chose when it loaded. Foreign client SDK paths therefore neither +select the wrong SDK nor cause a false restart. All other environment inputs +remain unchanged and participate in normal lifecycle checks. Protocol 0.10 permits a bounded retry only when a pre-execution command result explicitly carries `retryAfterRestart: true`. `executeWithDaemonRestartAsync` @@ -153,6 +370,136 @@ only an unstarted request can receive the typed retry authorization. Accepted queued requests drain their typed restart results before the old connection closes. +When the daemon's own installation was removed or replaced (for example a deleted +snapshot folder or a reinstalled Rush release), the daemon lets its running requests +finish, answers each other request with that typed restart once they have, and then +exits. The timeout rules of a restart for the request's environment apply (see +above): the built-in default does not limit waiting for the requests that were running +when the command arrived, but still limits it while the daemon runs a `rushx` script, +and `--no-wait` and an explicit `--wait-timeout` limit the whole wait. A command that +times out exits with code 1, names the changed folder and, if a script runs, suggests +stopping it. Otherwise the client starts a daemon from its own launcher once the wait +ends, resubmits the request, and prints one line on stderr (or above the agent +progress rows): `rush-client: The daemon's installation at was removed; +restarted the daemon (PID ).` + +While a command waits for a daemon restart, for its installation, the command's +environment or the workspace's inputs, the agent progress status (or stderr, on a +terminal and on a pipe) says what it waits for and why the daemon restarts, as soon as +the daemon reports the wait: `rush-client: waiting for 2 running requests to finish, +including 1 rushx script; the daemon (PID ) then restarts, because +common/config/rush/pnpm-lock.yaml changed.` After `because`, the cause is `its +installation at was removed` (or `replaced`), `this request's environment +differs from the daemon's in NODE_OPTIONS` (variable names, never their values), +` changed` for the workspace's installation, `the code of Rush or a Rush plugin +changed ()`, or `this request selects Rush `. The count names `rushx` +scripts, because a script such as a dev server may run until it is stopped. A +`rushx-client` script that waits for another request's restart prints `rushx-client: +waiting for the daemon (PID ) to restart for another request (2 requests ahead), +because .` The line goes to the stderr that the script writes to, which is a +pipe, because a `rushx-client` with a terminal runs the script in-process. Its first line +comes once `rushx-client` itself has started and sent the request, which takes about half a +second on a busy machine. A native `install` or `update` restarts the daemon once it ends, +unless it fails before it changes the installation (for example on the Rush lock), and +that restart would end the `rushx` scripts that the daemon runs. The daemon can't tell +beforehand whether the command will fail, so it first waits for them and says so the +same way: `rush-client: waiting for 1 running rushx script to finish, since this command +restarts the daemon (PID ), which would end it.` A terminal +gets a line whenever the wait changes, and a pipe when the wait begins or its cause +changes. Both get the line again with the time waited (`still waiting after 25s for +…`) whenever 25 seconds pass without one, until the command follows the restart, +starts, or ends. Once the client asks rushd to cancel the command (Ctrl+C) and says so, +it writes no more wait lines. In agent mode, the progress phase says it, and on a pipe +a status line is written at once when the wait begins or its cause changes. + +When the daemon restarts for a command's environment, the client prints a line of the +same kind that names the variables that differed, never their values: +`rush-client: A command's environment differed from the daemon's in NODE_OPTIONS; +restarted the daemon (PID ).` It names at most four variables and then says how +many more differed (`A, B, C, D and 2 more`). A command that was waiting when another +command's environment restarted the daemon prints that command's variables, because the +successor starts with that command's environment. A daemon that does not name the +variables gets no line. A variable name that holds a control character, such as a newline +or ESC, is printed with that character written as a `\xHH` escape, so the line stays one +line. + +If the daemon that replaces it does not start, after either kind of restart, the command +fails with exit code 1 and one line that gives the reason for the restart before the +startup error: `rush-client: A command's environment differed from the daemon's in +NODE_OPTIONS; the restarted daemon did not start: `. A variable that keeps +the daemon from starting is then among the names in that line. + +The client leaves out the `…; restarted the daemon (PID ).` line of either kind when +the command already wrote a wait line (see above) for the same cause since it last +restarted, because that line said why the daemon restarts: the same variables, or the same +change to the same installation. The wait line counts when it was written as a line, on +stderr or as an agent status line on a pipe. With agent output on a terminal, the wait is +only in the live rows, so the line is printed above them. + +When the connection is lost before a command's result, the command fails with exit code 1 +and is not retried. The diagnostic keeps "Daemon disconnected before delivering a result; the +command was not retried." and says what happened to rushd. If its process exited (a crash, an +out-of-memory kill or a signal), it names the PID, points to `rush-client daemon logs` and, if +the daemon exits again, to `--no-daemon` (`rushx-client --no-daemon` for Rushx), and quotes on a +second line the fatal error that the launcher log recorded after the command was sent. Before it +prints that, the client stops the operations that the exited daemon left running and removes its +ownership record and socket, as the next daemon start would, so a rerun, with or without +`--no-daemon`, does not race them. +Rush run in-process, with `--no-daemon` or as a fallback, first does the same when the ownership +record names a daemon that no longer runs, for example when the client that ran the command was +killed along with the daemon. On Linux that includes a daemon that has exited but is not reaped yet: +the client waits up to 1 second for it to be reaped, and else runs Rush without the reclaim, which a +later command does once the daemon's parent reaps it. Each of these reclaims, and the ones that `daemon start`, an automatic +start and `daemon stop --force` do, prints one line that says what it stopped, for example: + +``` +rush-client: Stopped the operations that the exited daemon (PID 4242) left running (process group 4242). +``` + +The line begins "Killed" instead, and ends "they did not exit after SIGTERM", when an operation +needed SIGKILL. While agent mode shows its progress lines, the line is written among them. +If rushd still runs, it says that only the connection closed. Ctrl+C and an orderly `daemon stop` +or `daemon restart` still end a command as cancelled (exit code 130). + +A command that was still waiting in rushd's queue when rushd exited has not run, if rushd says +when it starts a command (protocol 0.14) and had not said so. The client then sends it to a new +daemon once, within its `--wait-timeout`, and before that daemon starts it prints one line on +stderr (or above the agent progress rows): `rush-client: rushd (PID ) exited while the +command was queued; sending the command to a new daemon.` In agent mode the phase, and the status +lines on a pipe, then read `request resubmitted to the new daemon; preparing the workspace graph` +rather than what the command waited for in the exited daemon's queue, until the new daemon reports +a queue position or starts the command. If the new daemon does not start, +the command fails after that line with the startup error. If the connection to the new daemon +is lost too, the diagnostic begins "Daemon disconnected before delivering a result; the command +was already sent to a new daemon once." Commands that waited together reach the new daemon in +the order in which their clients noticed that rushd exited, not in their order in the queue. + +While a command runs, the client checks that rushd still responds. Once rushd has sent nothing +for 10 s, the client pings it. Once it has sent nothing for 30 s, not even the reply, for example +because its process was stopped, the client says so at once and says what that means: +`rush-client: rushd (PID ) has not responded for 30s; its process may be stopped or +overloaded; on Linux, "rush-client daemon status" says which. This command goes on if rushd +responds; interrupt it (Ctrl+C) to stop waiting.` When +rushd sends anything again, a second line says so: `rush-client: rushd (PID ) responded again +after 70s.` In agent mode, the progress phase says it; on a pipe both lines are written at once, +and until rushd responds, the status lines say how long it has not responded instead of what runs. +Time in which the client itself was stopped or busy writing output does not count. An interrupt +asks rushd to cancel the command, which a stopped rushd cannot confirm, so the client stops waiting +when its 5 s cancellation wait ends, and writes no more of these lines once it asked. Daemons older +than protocol 0.13 are not checked. + +When the process that reads the client's output exits first, for example `head` in +`rush-client build | head -5`, the client's next write to that stream fails with EPIPE. The client +does not report that as a lost connection. It asks rushd to cancel the command, waits for the stop +as it does after Ctrl+C, and exits with code 141 (128 + SIGPIPE), which a shell reports for a writer +that SIGPIPE ended. Instead of the cancelling and cancelled lines it prints one line, which names +the stream: `rush-client: build cancelled, because the process reading its stdout exited (EPIPE).` +In `rushx-client` it begins with `rushx-client:`. +Agent output prints the same line on stderr. The client only learns of the exit at its next write, +which in agent output on a pipe can be the next status line, up to 25 s later. A command whose +result arrived before a write failed keeps the result's exit code. Rush run in-process and +`rush-client daemon logs` still report a failed write as an error. + Piped input uses protocol 0.7's negotiated stdin admission and EOF. The client does not read input until the command attaches an input destination, and sends bounded chunks only as the daemon grants write credits. EOF follows all preceding writes; @@ -184,12 +531,17 @@ keys and unknown `RUSH_DAEMON*` variables fail validation. | `enabled` | `RUSH_DAEMON` | false | Client routing | | `autoStart` | `RUSH_DAEMON_AUTO_START` | true | Only after opt-in | | `idleTimeoutSeconds` | `RUSH_DAEMON_IDLE_TIMEOUT_SECONDS` | 900 | Host idle shutdown after request/output/cleanup drain | -| `queueTimeoutSeconds` | `RUSH_DAEMON_QUEUE_TIMEOUT_SECONDS` | 30 | Admission wait limit. The default does not bound waiting behind a running compatible build; an explicit value does | +| `queueTimeoutSeconds` | `RUSH_DAEMON_QUEUE_TIMEOUT_SECONDS` | 30 | Admission wait limit. Time behind another request's graph load (up to 10 times the limit) and the request's own work do not count. The default does not limit waiting behind a running compatible build, or, for a daemon restart, behind requests that were already running (while no `rushx` script is running); an explicit value does | | `watch` | `RUSH_DAEMON_WATCH` | false | Persistent host observation of requested warm projects; false keeps root/config guards only. Never schedules builds | | `usePersistentIpcRunners` | `RUSH_DAEMON_USE_PERSISTENT_IPC_RUNNERS` | false | Enables explicit per-operation `daemonIpc` Node launchers for unsharded incremental daemon builds | +| `incrementalBuilds` | `RUSH_DAEMON_INCREMENTAL_BUILDS` | true | Runs an operation's `:incremental` script instead of its initial script when only files it builds were edited since its last successful run in the daemon and its output folders are unchanged. Additions, deletions, renames, configuration, tool, environment and command-line changes, bundled outputs, cache restores and native Rush commands run the initial script. Incremental results are never written to the build cache | +| `warmWorkers` | `RUSH_DAEMON_WARM_WORKERS` | false | With `incrementalBuilds`, keeps a watch-mode worker (the `:incremental:ipc` script) alive between builds for each operation whose `rush-project.json` operation settings set `allowDaemonWarmWorker`, and sends it the next incremental run. Opt in only if that script runs every task and check that the initial script runs, for Heft including lint and API Extractor. When an incremental run is not allowed, the worker is closed and the initial script runs. Workers count toward `warmMemoryBudgetMB` and `warmSetMaxProjects`, so raise both to keep them alive | +| `joinRunningBatch` | `RUSH_DAEMON_JOIN_RUNNING_BATCH` | false | Experimental. A build request that arrives while the daemon executes an incremental batch with the same request settings adds its operations to the executing iteration and gets its result once they complete, instead of waiting for the iteration to end. When the iteration can't take its work, the request waits as before | +| `deferCacheWrites` | `RUSH_DAEMON_DEFER_CACHE_WRITES` | false | Lets an operation complete once its output files are cloned, and writes its build cache entry from the clones in the background. Needs a file system that can clone files (such as Btrfs, XFS or APFS); otherwise, and in cobuilds, the entry is written before the operation completes. A failed background write doesn't change the operation's status; the next command reports it in a warning (on stderr in legacy output), which agent output also prints. A restore of the entry's key misses until the entry is written, and `daemon stop` drops the entries that aren't written yet | +| `backgroundPrepare` | `RUSH_DAEMON_BACKGROUND_PREPARE` | false | Experimental. When a watched workspace input changes so that the next request would reload the workspace graph (`lastReloadTier` 1), for example after an edit of `rush.json` or `common/config/rush/command-line.json`, an idle daemon reloads it and creates the engine for the command line of the last phased command that it served, 2 seconds after the last change. It never runs an operation or restarts the daemon, and it doesn't start while another Rush process holds the repository lock. A request with the same command line waits for it; any other request stops it and runs as before | | `warmIdleTimeoutSeconds` | `RUSH_DAEMON_WARM_IDLE_TIMEOUT_SECONDS` | 300 | Idle runner, project-watcher and retained-result eviction | -| `warmMemoryBudgetMB` | `RUSH_DAEMON_WARM_MEMORY_BUDGET_MB` | 512 | Best-effort sampled RSS budget in MiB, not a hard ceiling | -| `warmSetMaxProjects` | `RUSH_DAEMON_WARM_SET_MAX_PROJECTS` | 20 | Best-effort retained-project limit; never trims requested execution | +| `warmMemoryBudgetMB` | `RUSH_DAEMON_WARM_MEMORY_BUDGET_MB` | 512 | Best-effort sampled RSS budget in MiB, not a hard ceiling. Compared against whole-daemon RSS plus measured child RSS, so keep it above the daemon baseline (~130-190 MiB) | +| `warmSetMaxProjects` | `RUSH_DAEMON_WARM_SET_MAX_PROJECTS` | 20 | Best-effort limit on projects holding warm resources (active runners, watchers); retained results of resource-free projects do not count. Never trims requested execution | | `autoWarmByTelemetry` | `RUSH_DAEMON_AUTO_WARM_BY_TELEMETRY` | false | Measured retention ranking with conservative LRU fallback; no speculative scripts | For genuine persistent Node execution, enable `usePersistentIpcRunners` and add @@ -231,13 +583,80 @@ It does not replace a peer lacking safe shutdown support. Foreign package instal is a client preparation step; host self-restart selects only bundled or already cached compatible installations, never installing while the old workspace is being cleaned up. +Every client of a checkout finds its daemon in one per-user runtime folder: on Linux and +macOS, `/tmp/rushd-/`, whatever `TMPDIR` or `XDG_RUNTIME_DIR` a shell, job, service or +sandbox sets. It holds the socket (`.sock`), the ownership record +(`.pid.json`) and the launcher log. To move it, set `RUSHD_RUNTIME_DIR` to an absolute +path for every client of that checkout; the folder becomes `$RUSHD_RUNTIME_DIR/rushd-/`, +and its file system must support hard links. A relative `RUSHD_RUNTIME_DIR` is ignored. Clients +that disagree about `RUSHD_RUNTIME_DIR` use different folders, so each folder gets its own daemon +for the checkout. A client passes the folder to the daemon it starts. +The socket path must fit in a socket address: at most 108 bytes on Linux and 104 on macOS. +`RUSHD_RUNTIME_DIR` can therefore be at most 57 bytes on Linux and 53 on macOS, minus the +number of digits in your uid (50 bytes on Linux for uid 1234567). +Windows uses the named pipe `\\.\pipe\rushd-` and is unchanged. +The client refuses a runtime folder that is a symbolic link, is not a directory or belongs to +another user, and a `RUSHD_RUNTIME_DIR` too long for the socket path: commands run in-process +with that reason, and `daemon` commands exit 1. Remove the folder or change `RUSHD_RUNTIME_DIR`. +Auto-start likewise refuses a launcher log that is not a regular file of yours with one link, or +that it cannot open for writing, such as a symlink, a directory or a FIFO: commands run in-process +with that reason (for example `Launcher log cannot be opened for writing (ELOOP): `), and +`daemon start` exits 1. Remove the file. +A folder that others can open is made owner-only (`0700`). +Within one runtime folder, `TMPDIR`, `TMP`, `TEMP`, `XDG_RUNTIME_DIR` and `RUSHD_RUNTIME_DIR` +never select a different daemon; each operation receives the requesting client's values. +Clients and daemons before protocol 0.12 used `$XDG_RUNTIME_DIR/rushd-/` or the +temporary folder instead. A daemon started there stays there, where current clients do not +look, until it idles out or is stopped with that older client (`rush-client daemon stop`). +An older client that starts a current engine while `XDG_RUNTIME_DIR` or `TMPDIR` is set does +not find it and runs in-process, so upgrade `rush-cli-client` with the engine. +When a daemon before protocol 0.12 serves a current client, for example one listening in +`/tmp/rushd-/`, the client leaves `XDG_RUNTIME_DIR`, `TMPDIR`, `TMP` and `TEMP` out of +its requests on Linux and macOS, because that daemon would restart into the folder they name. +Its operations see the daemon's own values; stop it (`rush-client daemon stop`) to use yours. + `rush-client daemon status` only connects and checks hello/pong. It never starts a process, reclaims files, or treats a PID file as evidence of readiness. Both commands print one JSON object with `state: "ready"`, `socketPath`, and the actual pong fields (`uptimeMs`, available versions, optional `pid` and `residentMemoryBytes`, and an optional `workspace` snapshot). Exit code 0 means protocol readiness, not build support. An unreachable/incompatible endpoint, invalid -arguments, or startup failure returns exit code 1 with a diagnostic. +arguments, or startup failure returns exit code 1 with a diagnostic. When no daemon runs +at all, status also exits 1, and its diagnostic says `No daemon is running for +(Rush )` and what the next command does: with `enabled` and `autoStart`, the next +rush-client command that uses the daemon starts one; otherwise rush-client commands run Rush +in-process. This is the normal state after `daemon stop`, the idle timeout or SIGTERM. Status +reports it only when the endpoint refuses connections and neither an ownership record, a startup +reservation nor (outside Windows) a socket file remains; with any of these, the diagnostic +still says that it could not connect. When the endpoint +refuses connections and its ownership record (`.pid.json`) names a PID that no longer +exists, the diagnostic adds that rushd exited without shutting down (an orderly shutdown +removes the record) and that `daemon logs` may show why. When that PID still exists but the endpoint +refuses connections or does not complete hello/ping, the diagnostic adds what that process is doing, +on Linux for example `rushd (PID ) still owns .pid.json: it is stopped (state T), for example +by SIGSTOP, and it started 5 min ago.`, and on the next line what to do about it. +A client that lost its connection to that daemon, or that ran Rush in-process, removes the record +when it reclaims the daemon, and appends a line that names the daemon to the launcher log. Until a +daemon becomes ready again or `daemon stop --force` resets the workspace, the `No daemon is running` +diagnostic then adds `The last daemon, rushd (PID ), exited without shutting down; "rush-client +daemon logs" may show why.` +A daemon whose installation was removed or replaced still answers, but it restarts on +the next command: status then prints `state: "installationChanged"` with the pong's +`installationChange` (`change` and `folder`), a hint on stderr, and exits with code 1. + +A startup reservation (`.pid.json.starting`) refuses another daemon launch until +the daemon it reserved becomes ready. Status reports one that remains as +`startupReservation` with its `path`, the startup helper's `helperPid` when recorded, and +`helperState`: `running` (the helper still waits for readiness), `exited` (the helper will not +release it), or `unknown` (written by an older client). Status never removes it. Next to a +ready daemon, the next command that uses, stops or restarts that daemon removes it; when status +cannot connect, its diagnostic explains the reservation. After an `exited` helper, status also +reports `relaunchAfter`, 15 seconds after that helper was launched. Until then every automatic +start is refused at once (the command runs in-process), so that a daemon that fails the same way +each time, for example because of a configuration error, is not launched by every command. The +first command after it that finds nothing listening at the endpoint takes the reservation over +and starts the daemon again, and `daemon logs` shows a line saying so. `daemon logs` may also show +why the daemon did not become ready. The optional workspace snapshot reports the provider generation/token, graph existence, and available warm accounting without initializing a graph. Missing fields are unknown, @@ -262,19 +681,83 @@ attests a restart request, not completion of successor startup or success of a c `rush-client daemon stop` requires protocol >= 0.6 and waits for `shutdownAck` followed by EOF. It reports `state: "shutdownAccepted"` with exit code 0; this -does not assert successful workspace disposal. An absent/unreachable daemon, -unsupported protocol, missing acknowledgement, or timeout returns exit code 1. -It does not auto-start anything. +does not assert successful workspace disposal. Stop is idempotent: when nothing +listens at the endpoint and no daemon is starting, it reports `state: "notRunning"` with exit code 0. +When nothing listens but the ownership record names a process that still runs, for example a daemon +that removed its socket while it shuts down, stop first waits up to 15 seconds for that process to exit, +and says so on stderr once it has waited a second. If it still runs then, stop exits with code 1 and +says what that process is doing and what to do about it; a daemon that accepts the connection but does +not complete hello/ping gets the same diagnostic. On Linux, stop does not wait for a Rush daemon that +has this workspace's ownership record open and stays stopped (state T or t) while stop samples it for +1.5 s, because it cannot exit before something resumes it: stop exits with code 1 and that diagnostic +once the 1.5 s have passed. +While a daemon is still starting (its startup helper still runs, or another client holds the start +mutex), stop says so on stderr and waits up to 15 seconds for that daemon to become ready, then stops +it as below; reporting `notRunning` would leave it running afterwards. If it is still not ready by then, +stop exits with code 1 and leaves it running; run stop again once `daemon status` reports it ready. An +unsupported protocol, missing acknowledgement, handshake failure, or timeout +returns exit code 1. It does not auto-start anything. Before shutdown, it removes a startup +reservation that remains next to that daemon, as restart does, so that the reservation cannot +refuse the next start once the daemon is gone. It does so only for the live owner in the +ownership record, under the start mutex (waiting up to 15 seconds for it); a reservation that +it cannot resolve stays in place and is reported as `startupReservation`. + +`rush-client daemon stop --force` stops a running daemon the same way, then waits +(up to 15 seconds) for it to release its listener and ownership record and removes +any remaining artifacts, such as an abandoned startup reservation, reporting them in +`removedPaths`. When none is listening, it removes this workspace's leftover ownership record +(`.pid.json`), socket, and startup reservation (`.starting`), then reports +`state: "reset"` and the `removedPaths` (or `state: "notRunning"` if nothing was +left behind). It holds the start mutex, proves that no listener is bound, and +refuses (exit 1) while the recorded owner PID still exists and cannot be shown to +be a reused PID, saying what that process is doing, that no process was killed, and what to do about it. +It fails the same way, without killing that process, when that process accepts the connection but does +not complete hello/ping, for example because a signal stopped it. When the recorded owner PID no longer exists, the daemon exited without shutting +down and may have left operations running that only its records name, so the reset first stops them +as the next daemon start would: SIGTERM, then SIGKILL 2 seconds later, to the daemon's own process +group and to each operation process group that it recorded whose leader still has the recorded start +time (or has exited, while every live member of the group is in the group's own session and one of +them still has the `RUSHD_OPERATION_GROUPS` variable that the daemon gives the processes it starts). When a +process that started after the record was written has the recorded PID now, the daemon exited the same +way, so the reset stops the operation process groups that it recorded the same way, but never the +process group whose ID is that PID, which the later process may lead. It prints the +line shown above for a lost connection and reports what it stopped in `orphansReaped` (`daemonPid`, +`processGroupIds`, `outcome`). A recorded group that it cannot prove, such as a PID that a later +process now has, or a group whose leader has exited under a daemon from a release that did not set +`RUSHD_OPERATION_GROUPS`, is not signalled; its record is removed with the others. When such a group still +has a live process, the launcher log that `daemon logs` prints gets a line that names the group and the +daemon and says which check the group failed; nothing about it is printed. The reclaims after a lost +connection, before Rush runs in-process and before a daemon start do the same. If the operations cannot be +stopped, it exits with code 1 and removes nothing. While another process reclaims the same files it +also exits with code 1, except that after a shutdown it re-checks for up to 15 seconds. Otherwise it +never signals a process. Automatic startup already reclaims +the common leftovers on its own (see below); this is the documented escape hatch +that every fail-closed startup message points to. A reset also appends a line to the launcher log +that clears the report of a daemon that a client reclaimed, so status no longer names it. `rush-client daemon restart` first verifies that the selected Rush version has a launcher and captures the original lock's PID/start timestamp, checking that it matches pong's positive PID and the selected endpoint, then performs acknowledged -shutdown. It waits for original ownership release or a demonstrably dead owner -before calling the existing locked starter. A live/reused owner fails closed at +shutdown. Before shutdown, it removes a startup reservation that remains next to that +daemon (under the start mutex), so that the reservation cannot refuse the successor; if +another client holds the mutex for 15 seconds, restart fails without stopping the daemon. +It waits for original ownership release or a demonstrably dead owner +before calling the existing locked starter. A live owner fails closed at the startup deadline; no PID is killed and no live ownership record is deleted. A newly started/reused successor must pass hello/ping before reporting `state: "ready"`. -An absent daemon must be started explicitly with `daemon start`. +When nothing listens at the endpoint, restart starts a daemon exactly like `daemon start`. + +Automatic and explicit startup reclaim stale artifacts only when that is provably +safe: while holding the start mutex with no `.starting` reservation (or after taking over +one whose helper exited, as described above), a socket +without an ownership record, or an unreadable/corrupt record, is removed only after +a connection attempt is refused (so no listener exists). On Linux, a record whose +PID now belongs to a process that started after the record's `startedAt` (PID reuse) +is treated as dead; other platforms fail closed and point to `daemon stop --force`. Before such a +record is removed, the operation process groups that the daemon recorded are stopped as +`daemon stop --force` stops them, and the line shown above for a lost connection is printed; if they +cannot be stopped, the command fails and the record stays. Restart is explicit even when automatic startup or CI execution routing is disabled, but conflicts with `--no-daemon`. The two-phase host retains ownership @@ -291,6 +774,14 @@ parent closes its descriptor after spawning. On POSIX the launcher enforces mode `0600` and rejects linked destinations; Windows uses the existing per-user transport directory permissions. +Each daemon writes `