From 14bb6a4efaf73ce46c7de5c767d71cfd7a767524 Mon Sep 17 00:00:00 2001 From: TheLarkInn Date: Fri, 28 Aug 2026 09:12:12 +0000 Subject: [PATCH 1/3] Document experimental Rush reporters Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d6318e80-5da9-4858-a147-817e8692f10e --- apps/rush/README.md | 4 + apps/rush/UPGRADING.md | 12 + .../src/test/sandbox/reporter-demo/README.md | 33 +- ...orter-r10a-demo-docs_2026-08-28-09-20.json | 11 + ...orter-r10a-demo-docs_2026-08-28-09-20.json | 11 + docs/rush/reporter.md | 307 ++++++++++++++++++ libraries/reporter/README.md | 4 + 7 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 common/changes/@microsoft/rush/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json create mode 100644 common/changes/@rushstack/rush-reporter/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json create mode 100644 docs/rush/reporter.md diff --git a/apps/rush/README.md b/apps/rush/README.md index 336d835ef07..21e3ed293d1 100644 --- a/apps/rush/README.md +++ b/apps/rush/README.md @@ -64,6 +64,10 @@ _(If you don't have a GitHub account set up, you can use `rush install --bypass- For more details and support resources, please visit: https://rushjs.io +The repository also includes an +[experimental Rush reporter opt-in and demo guide](../../docs/rush/reporter.md). Rush 5 retains legacy +terminal output unless a reporter is explicitly enabled. + ## Links - [CHANGELOG.md]( diff --git a/apps/rush/UPGRADING.md b/apps/rush/UPGRADING.md index 000e3bbb3fc..b6726e4d9e7 100644 --- a/apps/rush/UPGRADING.md +++ b/apps/rush/UPGRADING.md @@ -1,5 +1,17 @@ # Upgrade notes for @microsoft/rush +### Experimental Rush reporter opt-in + +Rush 5 keeps the existing terminal output by default. Maintainers can evaluate the new reporter path for one +invocation with `--reporter=` or for a repository with `"useRushReporter": true` in +`common/config/rush/experiments.json`. + +Use `RUSH_REPORTER=legacy` for an immediate reporter-only rollback. Environment-based automatic AI selection +and the planned Rush 6 default change are not enabled yet. + +See the [experimental Rush reporter guide](../../docs/rush/reporter.md) for the control precedence, stdout and +stderr contracts, full-log and privacy behavior, cross-version prerequisite, and reproducible demo. + ### Rush 5.135.0 This release of Rush deprecates the `rush-project.json`'s `operationSettings.sharding.shardOperationSettings` diff --git a/apps/rush/src/test/sandbox/reporter-demo/README.md b/apps/rush/src/test/sandbox/reporter-demo/README.md index 7879a7bf71c..5eda0fe6669 100644 --- a/apps/rush/src/test/sandbox/reporter-demo/README.md +++ b/apps/rush/src/test/sandbox/reporter-demo/README.md @@ -1,9 +1,14 @@ # Direct Rush reporter demo -Build the three reporter projects, then run the self-checking direct invocation demo: +For the complete control, compatibility, privacy, and troubleshooting reference, see the +[experimental Rush reporter guide](../../../../../../docs/rush/reporter.md). + +From a clean checkout, install dependencies, build the reporter path, and run the self-checking direct +invocation demo: ```sh -rush build --to @microsoft/rush +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.mjs ``` @@ -18,6 +23,9 @@ transcript. Captured stdout/stderr files are written to a temporary folder. For an individual invocation: ```sh +# Interactive TTY only +node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=default + node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=plaintext node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json --log-level=debug node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=ai @@ -30,3 +38,24 @@ node apps/rush/bin/rush list --json --reporter=file Repositories can opt in without a command-line flag by setting `"useRushReporter": true` in `common/config/rush/experiments.json`. Remove that setting or use `RUSH_REPORTER=legacy` for immediate rollback. + +The expected output is shape-based: + +- legacy keeps the existing Rush banner, operation blocks, and final status sections; +- `default` uses a width-aware three-row live region and leaves a short final summary; +- explicit `plaintext` groups ordered output under `project (phase)` and prints the absolute full-log path; +- JSON stdout contains only NDJSON event envelopes; +- AI stdout contains `ai.status` and bounded `ai.final` records; +- file mode leaves stdout empty and writes the full-log path to stderr; +- quiet mode retains only the final result and full-log path. + +The intentional missing-project AI failure preserves an actionable diagnostic and complete log reference. +Structured remediation is included when the producing diagnostic supplies it; this parser failure does not +currently provide a remediation action. + +Inspect the latest complete log with: + +```sh +ls -lt common/temp/rush-logs +sed -n '1,120p' common/temp/rush-logs/latest.log +``` diff --git a/common/changes/@microsoft/rush/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json b/common/changes/@microsoft/rush/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json new file mode 100644 index 00000000000..653bac36e69 --- /dev/null +++ b/common/changes/@microsoft/rush/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush", + "comment": "Document the experimental reporter opt-in, rollback, output contracts, compatibility boundary, and reproducible demo.", + "type": "patch" + } + ], + "packageName": "@microsoft/rush", + "email": "TheLarkInn@users.noreply.github.com" +} diff --git a/common/changes/@rushstack/rush-reporter/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json b/common/changes/@rushstack/rush-reporter/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json new file mode 100644 index 00000000000..30a88aa9ea0 --- /dev/null +++ b/common/changes/@rushstack/rush-reporter/copilot-reporter-r10a-demo-docs_2026-08-28-09-20.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@rushstack/rush-reporter", + "comment": "Add the opt-in reporter guide covering built-in renderers, full logs, privacy, and rollout safety.", + "type": "patch" + } + ], + "packageName": "@rushstack/rush-reporter", + "email": "TheLarkInn@users.noreply.github.com" +} diff --git a/docs/rush/reporter.md b/docs/rush/reporter.md new file mode 100644 index 00000000000..d89cdd56271 --- /dev/null +++ b/docs/rush/reporter.md @@ -0,0 +1,307 @@ +# Experimental Rush reporters + +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. + +## Opt in and roll back + +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": true` to + `common/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: + +```sh +RUSH_REPORTER=legacy rush build +``` + +`RUSH_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](https://github.com/microsoft/rushstack/issues/5981) pass. + +## Command-line controls + +| Control | Current behavior | +| --- | --- | +| `--reporter=` | Selects `default`, `plaintext`, `json`, `ai`, `file`, or `legacy`. It may be specified once. | +| `--log-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=` | 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: + +```sh +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=file +``` + +Relative `--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. + +### Precedence and conflicts + +1. `RUSH_REPORTER=legacy` is the emergency override and wins over every + reporter opt-in. +2. An explicit non-legacy `--reporter` selects the primary reporter. +3. Without `--reporter`, `useRushReporter` selects a TTY/CI-aware primary + reporter. +4. 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. Repeating the same effective +level is allowed. + +`--output` and `--log-level` require an explicit non-legacy `--reporter` or the +repository experiment. They are not supported with `--reporter=legacy`. + +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. + +## Built-in reporters and stream ownership + +| 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. 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: + +```text +==[ @rushstack/rush-reporter (_phase:build) ]== +``` + +This distinction matters when one project participates in multiple phases. + +## Full-detail logs and privacy + +Every enabled reporter invocation attempts to create: + +```text +/common/temp/rush-logs/--.log +``` + +`common/temp/rush-logs/latest.log` points to, or copies, the latest completed +invocation. Logs older than 14 days are removed and each location retains at +most 20 sessions. `rush purge` removes the repository log directory. + +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-` 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. + +Reporter telemetry is allowlist-only. It excludes messages, paths, raw +stdout/stderr, command arguments, remediation parameters, stack traces, and all +`local-sensitive` or `secret` values. + +## Agent environment configuration + +Repositories can declare additional agent markers in `rush.json`: + +```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](https://github.com/microsoft/rushstack/issues/5981) +lands and its gates pass, select `--reporter=ai` explicitly. + +## Bootstrap and cross-version behavior + +> **Prerequisite:** The behavior in this section is implemented by +> [#5993](https://github.com/microsoft/rushstack/pull/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. + +Compatibility is intentionally asymmetric: + +- An **explicit** reporter request fails with an update-or-use-legacy message + when the installed frontend or reporter protocol cannot satisfy it. +- An **implicit** repository opt-in falls back to legacy output when the + installed version or protocol is incompatible. +- A new frontend loading an old engine keeps the old engine's legacy output + visible while adapting it into the host. +- 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. + +## Reproduce the repository demo + +From a clean Rush Stack checkout: + +```sh +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.mjs +``` + +The 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. + +The following individual commands are useful when reviewing each shape: + +```sh +# 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 + +# Final result and log path without operation output +node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=plaintext --quiet + +# Emergency rollback, even though a different reporter was requested +RUSH_REPORTER=legacy node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json +``` + +Expected shapes, rather than exact transcripts: + +- Legacy starts with the Rush banner and uses the existing numbered operation + blocks and final status sections. +- `default` maintains up to three live rows, then leaves a short success or + failure summary and log path. +- Explicit `plaintext` prints a `project (phase)` group, its ordered output, the + terminal operation status, the command result, and `Full log: `. +- `json` prints only NDJSON event envelopes. Final records include + `artifactAvailable`, `commandResult`, `commandCompleted`, and + `sessionCompleted`. +- `ai` prints an `ai.status` record and an `ai.final` record. The intentional + missing-project failure includes an actionable diagnostic summary and a + complete log reference. When a producer supplies structured remediation, it + appears in `diagnostics[].remediation`; the missing-project parser example + does not currently supply a remediation action. +- `file` leaves stdout empty and prints `Rush full log: ` on + stderr. +- `quiet` suppresses grouped operation output but preserves the final result and + log location. +- Rollback matches the feature-off transcript after normalizing run durations. + +Inspect the latest full log with: + +```sh +ls -lt common/temp/rush-logs +sed -n '1,120p' common/temp/rush-logs/latest.log +``` + +The log combines privacy-redacted event metadata with raw operation output +grouped by project and phase. + +## Troubleshooting + +| 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`. | +| Command-specific `--json` owns stdout | Use `--reporter=file` or omit the reporter. Do not combine it with a stdout reporter. | +| `RUSH_REPORTER=` cannot enable the pre-major path | Use `--reporter=`. 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 `common/temp/rush-logs` and the OS temp directory. The Rush command itself continues. | +| An explicit reporter fails across Rush versions | Update the global/bootstrap and repository Rush versions to compatible reporter protocol implementations, or use `--reporter=legacy`. This behavior lands with #5993. | +| Repository opt-in falls back to legacy across versions | This is the safe implicit compatibility behavior from #5993, not a daemon fallback. | + +## Rush 6 safety boundary + +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](https://github.com/microsoft/rushstack/pull/5993), the agent +auto-selection qualification in +[#5981](https://github.com/microsoft/rushstack/issues/5981), and the coordinated +migration work in [#5982](https://github.com/microsoft/rushstack/issues/5982). +The legacy renderer and `RUSH_REPORTER=legacy` rollback are planned to remain +available for at least that major. diff --git a/libraries/reporter/README.md b/libraries/reporter/README.md index b326abb9f19..968d2b430dd 100644 --- a/libraries/reporter/README.md +++ b/libraries/reporter/README.md @@ -4,6 +4,10 @@ Canonical event protocol, reporter manager, and built-in reporters for Rush. This package is released as a public beta. Exported contracts may change before the stable release. +Rush 5 keeps legacy terminal output by default. See the +[experimental Rush reporter guide](../../docs/rush/reporter.md) for opt-in controls, reporter behavior, +privacy boundaries, full-detail logs, bootstrap compatibility, and the reproducible repository demo. + ## Links - [CHANGELOG.md](https://github.com/microsoft/rushstack/blob/main/libraries/reporter/CHANGELOG.md) - Find out From 511538496c96f7348b48a8119fc4df4dc63256c0 Mon Sep 17 00:00:00 2001 From: TheLarkInn Date: Fri, 28 Aug 2026 09:34:45 +0000 Subject: [PATCH 2/3] Harden reporter documentation examples Make the demo independent of inherited reporter controls, verify exclusive output destinations, and correct log-level, alias-position, and latest-log guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d6318e80-5da9-4858-a147-817e8692f10e --- .../src/test/sandbox/reporter-demo/README.md | 10 ++++---- .../src/test/sandbox/reporter-demo/run.mjs | 21 ++++++++++++++++- docs/rush/reporter.md | 23 ++++++++++++++----- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/apps/rush/src/test/sandbox/reporter-demo/README.md b/apps/rush/src/test/sandbox/reporter-demo/README.md index 5eda0fe6669..3eb25f7d7c3 100644 --- a/apps/rush/src/test/sandbox/reporter-demo/README.md +++ b/apps/rush/src/test/sandbox/reporter-demo/README.md @@ -16,9 +16,11 @@ The script runs the same `rush build --only @rushstack/rush-reporter` operation plaintext, JSON, AI, file, and quiet modes, plus parser failure, help, and command-specific JSON cases. It verifies payload-only machine stdout, one visible writer, ordered/lossless plaintext grouping from a same-invocation JSON sidecar, final artifact completeness, owner-only log permissions, failure flushing, -AI parser-error context, command-JSON ownership, CI plaintext output, cache-path output, normalized -`RUSH_TEMP_FOLDER` log placement, matching purge-path selection, and the `RUSH_REPORTER=legacy` rollback -transcript. Captured stdout/stderr files are written to a temporary folder. +AI parser-error context, command-JSON ownership, exclusive sidecar destinations, and the +`RUSH_REPORTER=legacy` rollback transcript. Inherited `RUSH_REPORTER`, `RUSH_LOG_LEVEL`, and +`RUSH_QUIET_MODE` values are removed from the self-check matrix. It also verifies CI plaintext output, +cache-path output, normalized `RUSH_TEMP_FOLDER` log placement, and matching purge-path selection. +Captured stdout/stderr files are written to a temporary folder. For an individual invocation: @@ -53,7 +55,7 @@ The intentional missing-project AI failure preserves an actionable diagnostic an Structured remediation is included when the producing diagnostic supplies it; this parser failure does not currently provide a remediation action. -Inspect the latest complete log with: +After the demo exits, inspect the latest invocation log with: ```sh ls -lt common/temp/rush-logs diff --git a/apps/rush/src/test/sandbox/reporter-demo/run.mjs b/apps/rush/src/test/sandbox/reporter-demo/run.mjs index d61c29f4dad..2d51c935c7a 100644 --- a/apps/rush/src/test/sandbox/reporter-demo/run.mjs +++ b/apps/rush/src/test/sandbox/reporter-demo/run.mjs @@ -9,11 +9,15 @@ const repoRoot = path.resolve(scriptFolder, '..', '..', '..', '..', '..', '..'); const rushBin = path.join(repoRoot, 'apps', 'rush', 'bin', 'rush'); const outputFolder = fs.mkdtempSync(path.join(os.tmpdir(), 'rush-reporter-demo-')); const commonArgs = ['build', '--only', '@rushstack/rush-reporter']; +const baseEnv = { ...process.env }; +delete baseEnv.RUSH_REPORTER; +delete baseEnv.RUSH_LOG_LEVEL; +delete baseEnv.RUSH_QUIET_MODE; function run(name, args, env = {}, expectedStatus = 0) { const result = spawnSync(process.execPath, [rushBin, ...args], { cwd: repoRoot, - env: { ...process.env, ...env }, + env: { ...baseEnv, ...env }, encoding: 'utf8' }); fs.writeFileSync(path.join(outputFolder, `${name}.stdout`), result.stdout); @@ -82,6 +86,18 @@ const purgeLogMatch = tempPurge.stderr.match(/^Rush full log: (.+)$/m); if (!purgeLogMatch || purgeLogMatch[1].startsWith(tempOverride) || !fs.existsSync(purgeLogMatch[1])) { throw new Error('The active purge reporter log was not preserved outside RUSH_TEMP_FOLDER.'); } +const duplicateOutputPath = path.join(outputFolder, 'duplicate-output.jsonl'); +const outputConflict = run( + 'output-conflict', + [ + ...commonArgs, + '--reporter=plaintext', + `--output=json://${duplicateOutputPath}`, + `--output=file://${duplicateOutputPath}` + ], + {}, + 1 +); function parseNdjson(text, name) { if (text.includes('\u001b')) { @@ -216,6 +232,9 @@ if ( ) { throw new Error('Command-specific JSON ownership arbitration failed.'); } +if (outputConflict.stdout !== '' || !outputConflict.stderr.includes('is already owned by another reporter')) { + throw new Error('Duplicate reporter output destinations were not rejected.'); +} console.log(`Reporter demo outputs: ${outputFolder}`); console.log(`Full detail log: ${logMatch[1]}`); diff --git a/docs/rush/reporter.md b/docs/rush/reporter.md index d89cdd56271..24cddf2a33b 100644 --- a/docs/rush/reporter.md +++ b/docs/rush/reporter.md @@ -79,11 +79,17 @@ privacy-redacted NDJSON event records. A `file://` destination defaults to 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. Repeating the same effective -level is allowed. +`--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 compatibility aliases are global Rush options and must precede +the command name; for example, `rush --quiet build --reporter=plaintext`. 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 @@ -127,8 +133,10 @@ Every enabled reporter invocation attempts to create: /common/temp/rush-logs/--.log ``` -`common/temp/rush-logs/latest.log` points to, or copies, the latest completed -invocation. Logs older than 14 days are removed and each location retains at +`common/temp/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. Logs older than 14 days are removed and each location retains at most 20 sessions. `rush purge` removes the repository log directory. On platforms that support POSIX permissions, invocation logs and operation @@ -217,7 +225,9 @@ node apps/rush/src/test/sandbox/reporter-demo/run.mjs The 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. +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. The following individual commands are useful when reviewing each shape: @@ -241,7 +251,7 @@ node apps/rush/bin/rush build --only @rushstack/does-not-exist --reporter=ai 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 build --only @rushstack/rush-reporter --reporter=plaintext --quiet +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=json @@ -285,6 +295,7 @@ grouped by project and phase. | --- | --- | | `--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=` cannot enable the pre-major path | Use `--reporter=`. 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 `common/temp/rush-logs` and the OS temp directory. The Rush command itself continues. | From 5543e9b5191b6f3067b87bcc6306a823cf068189 Mon Sep 17 00:00:00 2001 From: TheLarkInn Date: Fri, 28 Aug 2026 22:02:48 +0000 Subject: [PATCH 3/3] Align reporter docs with reviewed behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d6318e80-5da9-4858-a147-817e8692f10e --- .../src/test/sandbox/reporter-demo/README.md | 11 +- .../src/test/sandbox/reporter-demo/run.mjs | 37 +++-- docs/rush/reporter.md | 130 +++++++++++++++--- 3 files changed, 143 insertions(+), 35 deletions(-) diff --git a/apps/rush/src/test/sandbox/reporter-demo/README.md b/apps/rush/src/test/sandbox/reporter-demo/README.md index 3eb25f7d7c3..764f3a4ccc9 100644 --- a/apps/rush/src/test/sandbox/reporter-demo/README.md +++ b/apps/rush/src/test/sandbox/reporter-demo/README.md @@ -19,8 +19,11 @@ same-invocation JSON sidecar, final artifact completeness, owner-only log permis AI parser-error context, command-JSON ownership, exclusive sidecar destinations, and the `RUSH_REPORTER=legacy` rollback transcript. Inherited `RUSH_REPORTER`, `RUSH_LOG_LEVEL`, and `RUSH_QUIET_MODE` values are removed from the self-check matrix. It also verifies CI plaintext output, -cache-path output, normalized `RUSH_TEMP_FOLDER` log placement, and matching purge-path selection. -Captured stdout/stderr files are written to a temporary folder. +cache-path output, a matching `RUSH_PREVIEW_VERSION`, normalized `RUSH_TEMP_FOLDER` log placement, and +matching purge-path selection. Captured stdout/stderr files are written to a temporary folder. + +The final matrix case invokes `rush purge` with an isolated `RUSH_TEMP_FOLDER`. This also unlinks project +dependencies, so run the install command again before continuing development in the checkout. For an individual invocation: @@ -30,8 +33,10 @@ node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=default node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=plaintext node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json --log-level=debug +RUSH_PREVIEW_VERSION=$(node -p "require('./apps/rush/package.json').version") node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=ai node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=file +RUSH_TEMP_FOLDER=./common/temp/reporter-demo-override node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=file node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=plaintext --log-level=quiet RUSH_REPORTER=legacy node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json node apps/rush/bin/rush list --json --reporter=file @@ -47,8 +52,10 @@ The expected output is shape-based: - `default` uses a width-aware three-row live region and leaves a short final summary; - explicit `plaintext` groups ordered output under `project (phase)` and prints the absolute full-log path; - JSON stdout contains only NDJSON event envelopes; +- a matching preview version keeps JSON stdout parseable and writes its warning to stderr; - AI stdout contains `ai.status` and bounded `ai.final` records; - file mode leaves stdout empty and writes the full-log path to stderr; +- `RUSH_TEMP_FOLDER` moves the full log and the matching purge removes that override; - quiet mode retains only the final result and full-log path. The intentional missing-project AI failure preserves an actionable diagnostic and complete log reference. diff --git a/apps/rush/src/test/sandbox/reporter-demo/run.mjs b/apps/rush/src/test/sandbox/reporter-demo/run.mjs index 2d51c935c7a..ab321630c30 100644 --- a/apps/rush/src/test/sandbox/reporter-demo/run.mjs +++ b/apps/rush/src/test/sandbox/reporter-demo/run.mjs @@ -7,6 +7,9 @@ import { fileURLToPath } from 'node:url'; const scriptFolder = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(scriptFolder, '..', '..', '..', '..', '..', '..'); const rushBin = path.join(repoRoot, 'apps', 'rush', 'bin', 'rush'); +const rushVersion = JSON.parse( + fs.readFileSync(path.join(repoRoot, 'apps', 'rush', 'package.json'), 'utf8') +).version; const outputFolder = fs.mkdtempSync(path.join(os.tmpdir(), 'rush-reporter-demo-')); const commonArgs = ['build', '--only', '@rushstack/rush-reporter']; const baseEnv = { ...process.env }; @@ -44,6 +47,9 @@ const plaintext = run('plaintext', [ `--output=json://${plaintextEventsPath}?logLevel=debug` ]).stdout; const json = run('json', [...commonArgs, '--reporter=json', '--log-level=debug']).stdout; +const previewJson = run('preview-json', [...commonArgs, '--reporter=json'], { + RUSH_PREVIEW_VERSION: rushVersion +}); const ai = run('ai', [...commonArgs, '--reporter=ai']).stdout; const file = run('file', [...commonArgs, '--reporter=file']); const quiet = run('quiet', [...commonArgs, '--reporter=plaintext', '--log-level=quiet']).stdout; @@ -66,6 +72,18 @@ const flagOffHelp = run('help-flag-off', ['--help']).stdout; const help = run('help', ['--help', '--reporter=json'], { RUSH_REPORTER: 'legacy' }).stdout; const commandJson = run('command-json', ['list', '--json', '--reporter=file']); const commandJsonConflict = run('command-json-conflict', ['list', '--json', '--reporter=json'], {}, 1); +const duplicateOutputPath = path.join(outputFolder, 'duplicate-output.jsonl'); +const outputConflict = run( + 'output-conflict', + [ + ...commonArgs, + '--reporter=plaintext', + `--output=json://${duplicateOutputPath}`, + `--output=file://${duplicateOutputPath}` + ], + {}, + 1 +); const tempOverride = path.join(outputFolder, 'rush-temp-override'); const tempOverrideFile = run('temp-override', [...commonArgs, '--reporter=file'], { RUSH_TEMP_FOLDER: tempOverride @@ -86,18 +104,6 @@ const purgeLogMatch = tempPurge.stderr.match(/^Rush full log: (.+)$/m); if (!purgeLogMatch || purgeLogMatch[1].startsWith(tempOverride) || !fs.existsSync(purgeLogMatch[1])) { throw new Error('The active purge reporter log was not preserved outside RUSH_TEMP_FOLDER.'); } -const duplicateOutputPath = path.join(outputFolder, 'duplicate-output.jsonl'); -const outputConflict = run( - 'output-conflict', - [ - ...commonArgs, - '--reporter=plaintext', - `--output=json://${duplicateOutputPath}`, - `--output=file://${duplicateOutputPath}` - ], - {}, - 1 -); function parseNdjson(text, name) { if (text.includes('\u001b')) { @@ -110,6 +116,7 @@ function parseNdjson(text, name) { } const jsonEvents = parseNdjson(json, 'json'); +const previewJsonEvents = parseNdjson(previewJson.stdout, 'preview-json'); const aiRecords = parseNdjson(ai, 'ai'); const failureJsonEvents = parseNdjson(failureJson, 'failure-json'); const failureAiRecords = parseNdjson(failureAi, 'failure-ai'); @@ -185,6 +192,12 @@ const aiFinal = aiRecords.at(-1); if (aiFinal?.kind !== 'ai.final' || aiFinal.log?.complete !== true) { throw new Error('AI output did not include a complete full-log reference.'); } +if ( + !previewJson.stderr.includes('RUSH_PREVIEW_VERSION') || + !previewJsonEvents.some((event) => event.type === 'sessionCompleted') +) { + throw new Error('The matching preview version did not preserve payload-only reporter stdout.'); +} const failureAiFinal = failureAiRecords.at(-1); if ( failureAiFinal?.result !== 'failed' || diff --git a/docs/rush/reporter.md b/docs/rush/reporter.md index 24cddf2a33b..36460281459 100644 --- a/docs/rush/reporter.md +++ b/docs/rush/reporter.md @@ -40,6 +40,12 @@ based automatic selection, including automatic `ai` selection, remains disabled until the evaluation gates in [#5981](https://github.com/microsoft/rushstack/issues/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. + ## Command-line controls | Control | Current behavior | @@ -81,10 +87,10 @@ Explicit CLI verbosity takes precedence over `RUSH_LOG_LEVEL`. 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 compatibility aliases are global Rush options and must precede -the command name; for example, `rush --quiet build --reporter=plaintext`. The -short `-v` flag is not a reporter verbosity alias because existing Rush commands -retain their established `-v` meanings. +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`. @@ -105,7 +111,7 @@ present. | --- | --- | --- | | `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. It emits no terminal control sequences. | +| `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. | @@ -125,19 +131,41 @@ example, the build operation for this package has the stable identity This distinction matters when one project participates in multiple phases. +### Watch iterations + +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. + ## Full-detail logs and privacy Every enabled reporter invocation attempts to create: ```text -/common/temp/rush-logs/--.log +/rush-logs/--.log ``` -`common/temp/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. Logs older than 14 days are removed and each location retains at -most 20 sessions. `rush purge` removes the repository log directory. +The Rush temp folder is normally `/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. + +`/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 @@ -150,9 +178,24 @@ 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. -Reporter telemetry is allowlist-only. It excludes messages, paths, raw -stdout/stderr, command arguments, remediation parameters, stack traces, and all -`local-sensitive` or `secret` values. +> **Prerequisite:** The detailed telemetry projection described below is +> implemented and reviewed in +> [#5990](https://github.com/microsoft/rushstack/pull/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. ## Agent environment configuration @@ -197,14 +240,29 @@ 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 reporter protocol cannot satisfy it. + 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. -- A new frontend loading an old engine keeps the old engine's legacy output - visible while adapting it into the host. +- 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. @@ -227,7 +285,9 @@ 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. +the matrix. 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. The following individual commands are useful when reviewing each shape: @@ -244,12 +304,18 @@ node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=plainte # Payload-only NDJSON on stdout node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json --log-level=debug +# Preview the bundled frontend version without contaminating JSON stdout +RUSH_PREVIEW_VERSION=$(node -p "require('./apps/rush/package.json').version") node apps/rush/bin/rush build --only @rushstack/rush-reporter --reporter=json + # 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 @@ -265,9 +331,11 @@ Expected shapes, rather than exact transcripts: failure summary and log path. - Explicit `plaintext` prints a `project (phase)` group, its ordered output, the terminal operation status, the command result, and `Full log: `. -- `json` prints only NDJSON event envelopes. Final records include +- `json --log-level=debug` prints only NDJSON event envelopes. Final records include `artifactAvailable`, `commandResult`, `commandCompleted`, and `sessionCompleted`. +- A matching `RUSH_PREVIEW_VERSION` leaves JSON stdout parseable and writes the + preview warning to stderr. - `ai` prints an `ai.status` record and an `ai.final` record. The intentional missing-project failure includes an actionable diagnostic summary and a complete log reference. When a producer supplies structured remediation, it @@ -275,9 +343,14 @@ Expected shapes, rather than exact transcripts: does not currently supply a remediation action. - `file` leaves stdout empty and prints `Rush full log: ` on stderr. +- `RUSH_TEMP_FOLDER` moves the log under that folder's `rush-logs` directory. + A subsequent reporter-enabled `rush purge` removes the override and preserves + its own purge log in OS temp. - `quiet` suppresses 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: @@ -298,10 +371,25 @@ grouped by project and phase. | 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=` cannot enable the pre-major path | Use `--reporter=`. 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 `common/temp/rush-logs` and the OS temp directory. The Rush command itself continues. | -| An explicit reporter fails across Rush versions | Update the global/bootstrap and repository Rush versions to compatible reporter protocol implementations, or use `--reporter=legacy`. This behavior lands with #5993. | +| The full-detail log cannot be written | Check the stderr warning and permissions for the active `/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. | +## Current limitations + +- Public reporter controls apply to `rush`, not `rushx` or `rush-pnpm`. +- Additional `--output` destinations are limited to `file://` and `json://`, + 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 by `iterationId`; 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. + ## Rush 6 safety boundary The planned Rush 6 migration may enable environment-based reporter selection by