For the daemon and reporter environment-variable reference, see Experimental daemon and reporter environment variables.
Rush 5 keeps its existing terminal output by default. The reporter system is a pre-major, explicit opt-in that can render the same command as an interactive display, append-only text, machine-readable events, an AI-oriented summary, or a file-only invocation.
The reporter system currently applies to rush commands. It does not consume
rushx or rush-pnpm arguments; for example, rush-pnpm --reporter remains a
PNPM option.
Choose one of these opt-in scopes:
- One invocation: pass an explicit non-legacy reporter, such as
rush build --reporter=plaintext. - One repository: add
"useRushReporter": truetocommon/config/rush/experiments.json.
If neither opt-in is present, Rush uses the legacy output path. The repository
experiment selects default for an interactive TTY, plaintext for CI or
redirected output, and file when a command-specific --json option already
owns stdout.
For an immediate reporter rollback, set:
RUSH_REPORTER=legacy rush buildRUSH_REPORTER=legacy overrides the repository experiment and reporter CLI
controls. It only changes reporting. It is separate from the Rush daemon's
typed in-process fallback, which decides where a command executes when the
daemon cannot satisfy terminal requirements.
Before the planned Rush 6 default change, other RUSH_REPORTER values do not
enable reporters. Use --reporter or useRushReporter instead. Environment
based automatic selection, including automatic ai selection, remains
disabled until the evaluation gates in
#5981 pass.
RUSH_PREVIEW_VERSION participates in the same pre-major safety check. A
preview that matches the bundled Rush frontend can use an explicit reporter,
with the preview warning remaining on stderr. If the preview selects a
different Rush engine version, an explicit non-legacy reporter fails rather
than silently changing the request; repository opt-in falls back to legacy.
| Control | Current behavior |
|---|---|
--reporter=<name> |
Selects default, plaintext, json, ai, file, or legacy. It may be specified once. |
--log-level=<level> |
Selects quiet, normal, verbose, or debug for the primary reporter. |
--quiet / -q |
Compatibility alias for reporter log level quiet. |
--verbose |
Compatibility alias for reporter log level verbose. |
--debug / -d |
Compatibility alias for reporter log level debug. |
--output=<uri> |
Adds a repeatable file destination. The current rollout accepts file:// and json:// URIs with an optional logLevel query parameter. |
Command-specific --json |
Keeps the command's existing JSON schema. It is not an alias for --reporter=json. |
Examples:
rush build --reporter=plaintext
rush build --reporter=json --log-level=debug
rush build --reporter=plaintext --output=json://./rush-events.jsonl?logLevel=debug
rush list --json --reporter=fileRelative --output paths are resolved from the invocation working directory.
At this rollout stage, both supported output URI schemes write
privacy-redacted NDJSON event records. A file:// destination defaults to
debug; a json:// destination inherits the primary log level unless
?logLevel= overrides it.
RUSH_REPORTER=legacyis the emergency override and wins over every reporter opt-in.- An explicit non-legacy
--reporterselects the primary reporter. - Without
--reporter,useRushReporterselects a TTY/CI-aware primary reporter. - Without either opt-in, the legacy output remains authoritative.
Explicit CLI verbosity takes precedence over RUSH_LOG_LEVEL.
RUSH_QUIET_MODE=1 and RUSH_QUIET_MODE=true remain quiet aliases.
Different explicit verbosity levels are rejected; for example,
--quiet --debug is invalid on the reporter path. --log-level itself may be
specified only once. Equivalent controls such as --quiet --log-level=quiet
are allowed. The frontend scans reporter controls until a standalone --;
arguments after that separator belong to the invoked command. The short -v
flag is not a reporter verbosity alias because existing Rush commands retain
their established -v meanings.
--output and --log-level require an explicit non-legacy --reporter or the
repository experiment. They are not supported with --reporter=legacy.
Each resolved --output destination must be unique. Two sidecars cannot own
the same path, even when one uses file:// and the other uses json://.
A command-specific --json option owns stdout. It can be combined with
--reporter=file, which leaves stdout to the command and prints the full-log
path on stderr. Combining it with --reporter=json, --reporter=ai,
--reporter=plaintext, or --reporter=default is rejected.
Rush help remains on the legacy parser-only path, even if reporter controls are present.
| Reporter | Intended use | Output contract |
|---|---|---|
default |
Interactive terminal | Owns stdout. Requires a TTY. Renders a width-aware three-row live region, restores the cursor, and leaves a bounded final summary. |
plaintext |
CI, redirected output, or a stable human-readable transcript | Owns stdout. Append-only, no cursor movement, and color disabled. Explicit selection and recognized CI use project-by-phase output groups; repository opt-in on another non-TTY uses a concise status form. |
json |
General automation | Owns stdout with one versioned event envelope per NDJSON line for each event admitted by the selected log level. Use --log-level=debug for the full event stream. It emits no terminal control sequences. |
ai |
Bounded agent consumption | Owns stdout with an ai.status record and a bounded ai.final record. The final record includes result, counts, diagnostics, optional structured remediation, and the completed full-log reference. |
file |
Preserve command stdout for another schema or suppress primary terminal rendering | Writes no stdout. The automatic full-detail log is created and its absolute path is printed to stderr. |
legacy |
Compatibility and emergency rollback | Uses the existing Rush output path and does not enable the new reporter operation stream. |
The default reporter uses the current terminal width, truncates long rows with
an ellipsis, and summarizes additional active projects as +N more. Color
follows TTY capability. NO_COLOR disables it, and FORCE_COLOR enables it
unless its value is 0 or false.
Reporter operation names are project x phase, not just project names. For
example, the build operation for this package has the stable identity
@rushstack/rush-reporter#_phase:build and a human header such as:
==[ @rushstack/rush-reporter (_phase:build) ]==
This distinction matters when one project participates in multiple phases.
In watch mode, the same project-by-phase operation can execute more than once.
Lifecycle and output events therefore carry a graph iterationId in their
payload. The built-in default, plaintext, AI, and file reporters use
iterationId together with the operation ID so overlapping scheduled
iterations cannot mix output, diagnostics, or totals.
Each watch summary reports only that iteration's completed and total operations. The AI final record likewise uses one recovered iteration scope for counts, failed projects, diagnostics, and errors rather than combining a failed cycle with a later successful cycle.
Every enabled reporter invocation attempts to create:
<rush-temp-folder>/rush-logs/<UTC timestamp>-<pid>-<action>.log
The Rush temp folder is normally <repo>/common/temp. If RUSH_TEMP_FOLDER is
set, Rush applies its normal path resolution and uses that same folder for
reporter logs and purge behavior.
This automatic invocation log always retains debug detail, including in
--reporter=file mode. Selecting a quieter primary --log-level does not
reduce the full-log artifact. For an additional filtered file, use a destination
such as --output=file://./summary.jsonl?logLevel=normal.
<rush-temp-folder>/rush-logs/latest.log points to, or copies, the current or
latest successfully opened invocation log. It can be incomplete while Rush is
still running; after the command exits, it contains the finalized log when
logging succeeded. When a new log is opened, logs older than 14 days are
removed and the location is capped at 20 sessions.
rush purge removes the active Rush temp folder, including its rush-logs
directory. A reporter-enabled purge writes that purge invocation's own log to
the owner-only OS-temp fallback instead, so it does not recreate the directory
that it just removed.
On platforms that support POSIX permissions, invocation logs and operation
spool files use mode 0600. If the repository path cannot be used, Rush falls
back to an owner-only rush-logs-<user> directory under the OS temp folder. If
both locations fail, Rush emits a one-line warning on stderr and continues the
command without a log artifact.
Structured values classified as secret are redacted from JSON and full-detail
logs. local-sensitive values, including absolute paths, remain in local
outputs. Raw child stdout/stderr is preserved in the full log and cannot be
reliably redacted, so treat the log as sensitive local build output.
Prerequisite: The detailed telemetry projection described below is implemented and reviewed in #5990.
Reporter telemetry is a bounded allowlist projection, not a serialized reporter
event stream. Envelope metadata, lifecycle values, protocol ownership, and
producer identity are collected only from effectively public events. A
diagnostic with any non-public parameter is treated as non-public even when its
envelope floor is public.
For a non-public diagnostic, telemetry may retain only a centrally registered
diagnostic code and that code's registry category. It does not retain the
diagnostic parameters, remediation, templates, source, message text, or
producer identity. Messages, paths, raw stdout/stderr, command arguments,
artifacts, extension payloads, remediation parameters, stack traces, and other
local-sensitive or secret values are excluded. Diagnostic and producer
dimensions have deterministic count and string-length budgets, with trusted
registered or parent-session values prioritized.
Repositories can declare additional agent markers in rush.json:
{
"reporting": {
"agentEnvironmentVariables": ["MY_AGENT_CLI", "ANOTHER_AGENT"]
}
}COPILOT_CLI is the built-in marker and does not need to be listed. A marker is
considered active when it is defined and is not an empty string, 0, false,
no, or off, ignoring case.
This configuration is available to the reporter selection API, but the current
Rush 5 frontend deliberately does not automatically select ai from these
variables. Until #5981
lands and its gates pass, select --reporter=ai explicitly.
Prerequisite: The behavior in this section is implemented by #5993. It is not provided by the direct-invocation demo branch alone.
A direct rush invocation starts in the Rush frontend and does not create a
bootstrap handoff file.
After #5993 lands, common/scripts/install-run-rush.js and generated
install-run-rush launchers parse the early reporter opt-in before installing
or loading the repository's Rush version. Startup and inherited package-manager
output are buffered into an owner-only, nonce-authenticated NDJSON handoff in
the OS temp folder. The installed frontend replays the records in order, deletes
the handoff, clears its private environment variables, and sweeps abandoned
handoffs older than 14 days.
The handoff buffer is limited to 1 MiB and raw output chunks to 64 KiB. Replaceable status updates can be dropped with a truncation marker. If required output cannot be preserved, bootstrap fails rather than presenting incomplete output as successful.
The bootstrap does not publish the working directory or full command arguments
as public events. Ordinary status can be public, while messages containing
lockfile, npmrc, installation, or other filesystem paths are classified
local-sensitive. Raw package-manager output is also local-sensitive.
Capture corruption or a partial final record produces one local-sensitive
warning on stderr, is omitted from required handoff accounting, and does not
replace the real install result. Machine reporter stdout remains payload-only
on bootstrap failure.
Compatibility is intentionally asymmetric:
- An explicit reporter request fails with an update-or-use-legacy message when the installed frontend or bootstrap protocol cannot satisfy it.
- An implicit repository opt-in falls back to legacy output when the installed version or protocol is incompatible.
- The current direct frontend also rejects an explicit non-legacy reporter when version selection chooses a different engine, because it cannot verify that engine's reporter close contract. It does not strip the option or pretend the request succeeded.
- The #5993 new-frontend/old-engine adapter is a lower-level compatibility primitive: when such a pairing is allowed, it preserves the old engine's stdout/stderr ordering and mirrors the output into the host. Machine reporter stdout stays payload-only, so old-engine stdout is visible on stderr instead.
- An old frontend loading a new engine receives legacy engine rendering because it cannot supply a structured sink.
These compatibility fallbacks concern frontend/engine or bootstrap version
skew. They are different from both RUSH_REPORTER=legacy and the daemon's
in-process execution fallback.
From a clean Rush Stack checkout:
node common/scripts/install-run-rush.js install
node common/scripts/install-run-rush.js build --to @microsoft/rush
node apps/rush/src/test/sandbox/reporter-demo/run.mjsThe self-checking script prints a temporary output directory and the full-detail
log path. It exercises the legacy baseline, detailed plaintext, JSON, AI, file,
quiet, rollback, parser failure, help, explicit sidecar output, and
command-specific JSON ownership. It removes inherited RUSH_REPORTER,
RUSH_LOG_LEVEL, and RUSH_QUIET_MODE values so those controls do not change
the matrix, and sets RUSH_PREVIEW_VERSION to the locally built package version
so it exercises that frontend and engine rather than an older rush.json pin.
It also runs rush purge against an isolated RUSH_TEMP_FOLDER to verify
cleanup. Because rush purge unlinks project dependencies, run the install
command again before continuing development in this checkout.
For the individual commands below, first select the locally built engine in the
same shell. Invoking apps/rush/bin/rush still honors the version in rush.json;
without this override, it can select an older engine and reject the explicit
reporter request. This override only selects the engine: reporter activation
still requires --reporter or the repository experiment.
export RUSH_PREVIEW_VERSION="$(node -p "require('./apps/rush/package.json').version")"
# Legacy baseline: unchanged when no opt-in is present
node apps/rush/bin/rush build --only @rushstack/rush-reporter
# Compact interactive reporter; run directly in a TTY
node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=default
# Append-only project-by-phase groups
node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=plaintext
# Payload-only NDJSON on stdout
node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json --log-level=debug
# Bounded AI failure record; this command intentionally exits with code 1
node apps/rush/bin/rush build --only @rushstack/does-not-exist --reporter=ai
# No stdout; the generated full-log path is written to stderr
node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=file
# Put logs under a normalized Rush temp override
RUSH_TEMP_FOLDER=./common/temp/reporter-demo-override node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=file
# Final result and log path without operation output
node apps/rush/bin/rush --quiet build --only @rushstack/rush-reporter --reporter=plaintext
# Emergency rollback, even though a different reporter was requested
RUSH_REPORTER=legacy node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=jsonAfter the individual demo commands, run unset RUSH_PREVIEW_VERSION to return to
the repository's normal version selection. The self-checking driver sets its
own override and does not require the shell export.
Expected shapes, rather than exact transcripts:
- Legacy starts with the Rush banner and uses the existing numbered operation blocks and final status sections.
defaultmaintains up to three live rows, then leaves a short success or failure summary and log path.- Explicit
plaintextprints aproject (phase)group, its ordered output, the terminal operation status, the command result, andFull log: <absolute path>. json --log-level=debugprints only NDJSON event envelopes. Final records includeartifactAvailable,commandResult,commandCompleted, andsessionCompleted.- A matching
RUSH_PREVIEW_VERSIONleaves JSON stdout parseable and writes the preview warning to stderr. aiprints anai.statusrecord and anai.finalrecord. The intentional missing-project failure includes an actionable diagnostic summary and a complete log reference. When a producer supplies structured remediation, it appears indiagnostics[].remediation; the missing-project parser example does not currently supply a remediation action.fileleaves stdout empty and printsRush full log: <absolute path>on stderr.RUSH_TEMP_FOLDERmoves the log under that folder'srush-logsdirectory. A subsequent reporter-enabledrush purgeremoves the override and preserves its own purge log in OS temp.quietsuppresses grouped operation output but preserves the final result and log location.- Rollback matches the feature-off transcript after normalizing run durations.
- Watch summaries identify one graph iteration and report per-iteration totals, even when scheduling overlaps the prior cycle's completion.
Inspect the latest full log with:
ls -lt common/temp/rush-logs
sed -n '1,120p' common/temp/rush-logs/latest.logThe log combines privacy-redacted event metadata with raw operation output grouped by project and phase.
| Symptom | Resolution |
|---|---|
--reporter=default requires an interactive TTY |
Use --reporter=plaintext in CI, a pipe, or redirected output. |
--output or --log-level requires an opt-in |
Add an explicit non-legacy --reporter, or enable useRushReporter. |
| A destination is already owned by another reporter | Give each repeatable --output sidecar a different resolved path. URI scheme differences do not permit sharing one path. |
Command-specific --json owns stdout |
Use --reporter=file or omit the reporter. Do not combine it with a stdout reporter. |
RUSH_REPORTER=<non-legacy> cannot enable the pre-major path |
Use --reporter=<name>. Only RUSH_REPORTER=legacy is active before the auto-selection gates pass. |
| The full-detail log cannot be written | Check the stderr warning and permissions for the active <rush-temp-folder>/rush-logs and the OS temp directory. The Rush command itself continues. |
Logs are not under common/temp |
Check RUSH_TEMP_FOLDER; reporter logs follow the same normalized override used by Rush and rush purge. |
| An explicit reporter fails across Rush versions | Use the Rush version bundled with the frontend, update the global/bootstrap and repository versions together, or use --reporter=legacy. The bootstrap-specific checks land with #5993. |
| Repository opt-in falls back to legacy across versions | This is the safe implicit compatibility behavior from #5993, not a daemon fallback. |
- Public reporter controls apply to
rush, notrushxorrush-pnpm. - Additional
--outputdestinations are limited tofile://andjson://, and both currently write NDJSON. - Third-party reporter loading remains deferred.
- The exported
regroupOperationOutput()convenience helper groups by operation ID only. A third-party watch reporter using it directly must first partition events byiterationId; the built-in reporters already isolate iterations. - The bounded telemetry aggregate does not currently expose a truncation indicator when a diagnostic or producer dimension reaches its budget.
- Automatic agent and AI selection remains disabled until #5981 passes.
The planned Rush 6 migration may enable environment-based reporter selection by default, align reporter ownership with daemon clients, gate incompatible plugins, and remove legacy terminal APIs. None of those defaults are enabled by this opt-in guide.
The Rush 6 change remains gated on the bootstrap/cross-version work in
#5993, the agent
auto-selection qualification in
#5981, and the coordinated
migration work in #5982.
The legacy renderer and RUSH_REPORTER=legacy rollback are planned to remain
available for at least that major.