Skip to content

Commit b0a511d

Browse files
TheLarkInnCopilot
andauthored
Reporter overhaul (6/6): performance budgets and daemon-aligned major default flip (#5870)
* Add reporter performance and capacity budgets (feature 27/28) Encode the specification §7.3 blocking budgets (3% wall-time regression, 32 MiB peak memory, 10 Hz interactive refresh, 64 KiB AI output, 20 AI detailed diagnostics) as shared data in a new perf module, with helpers for benchmark harnesses and capacity tests. Add a getPendingEventCount observability hook to ReporterManager to prove bounded streaming, and a Performance test suite covering the budgets, bounded streaming, a high-volume benchmark, queue-pressure protected-event preservation, and status coalescing. Assistant-model: GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 897dcf7e-e6e8-4a84-85ca-34b93fa29be3 * Add rush change file for reporter performance budgets Assistant-model: GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 897dcf7e-e6e8-4a84-85ca-34b93fa29be3 * Add daemon-aligned major default-flip migration model (feature 28/28) Encode the specification §8.1 phase 6 default flip as revertible data in a new migration module: the seven migration phases (each independently releasable and revertible), pre-flip and daemon-aligned major default sets (automatic selection on by default, legacy terminal APIs removed, incompatible plugins gated before apply, legacy renderer/aliases/sentinel bridge retained, RUSH_REPORTER=legacy emergency fallback), and a plugin apply gate that fails incompatible plugins with a structured RUSH_PLUGIN_API_INCOMPATIBLE diagnostic. Completes the Rush Reporter Overhaul (28/28). Assistant-model: GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 897dcf7e-e6e8-4a84-85ca-34b93fa29be3 * Add rush change file for daemon-aligned major migration model Assistant-model: GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 897dcf7e-e6e8-4a84-85ca-34b93fa29be3 * Align performance-budget tests with manager-derived required flag The coalescing-pressure test now proves protection with operationStatusChanged (protected by type) instead of a producer-set required flag, which the sink no longer accepts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Fix reporter change file package names Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix reporter migration fallback diagnostics Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Align reporter migration gate with Rush version ranges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address reporter parity review feedback Complete lifecycle status coverage, root-session parity, telemetry compatibility, extension privacy, and Rush semver-range plugin gating. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address performance and migration review feedback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 48b21772-7262-40a9-9524-c2b21582d201 * Stabilize reporter performance budget benchmark Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 48b21772-7262-40a9-9524-c2b21582d201 * Make reporter benchmark resilient to CI contention Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 48b21772-7262-40a9-9524-c2b21582d201 --------- Co-authored-by: Sean Larkin <TheLarkInn@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 897dcf7e-e6e8-4a84-85ca-34b93fa29be3 Copilot-Session: 48b21772-7262-40a9-9524-c2b21582d201
1 parent 0c0b2bf commit b0a511d

34 files changed

Lines changed: 1666 additions & 129 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@microsoft/rush",
5+
"comment": "Allow Rush plugin manifests to declare an optional supported Rush version range.",
6+
"type": "patch"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "TheLarkInn@users.noreply.github.com"
11+
}

common/changes/@rushstack/rush-reporter/docs-rush-reporter-overhaul-spec_2026-07-15-00-18-26.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"changes": [
33
{
44
"packageName": "@rushstack/rush-reporter",
5-
"comment": "Add scoped session reporting (createScopedReporter, RushSessionReporting, IScopedLogger, execution context) and plugin API version compatibility with a migration diagnostic",
5+
"comment": "Add scoped session reporting (createScopedReporter, RushSessionReporting, IScopedLogger, execution context) and Rush version range compatibility with a migration diagnostic",
66
"type": "minor"
77
}
88
],
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/rush-reporter",
5+
"comment": "Add reporter performance and capacity budgets: a perf module encoding the specification blocking budgets with wall-time and memory helpers, plus a ReporterManager.getPendingEventCount observability hook for bounded streaming",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/rush-reporter",
10+
"email": "TheLarkInn@users.noreply.github.com"
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@rushstack/rush-reporter",
5+
"comment": "Add the daemon-aligned major default-flip migration model: the reporter migration phases, pre-flip and post-flip major default sets, and a plugin apply gate that fails incompatible plugins before apply() with a structured migration diagnostic",
6+
"type": "minor"
7+
}
8+
],
9+
"packageName": "@rushstack/rush-reporter",
10+
"email": "TheLarkInn@users.noreply.github.com"
11+
}

common/config/subspaces/default/pnpm-lock.yaml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/reviews/api/rush-reporter.api.md

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,14 @@ export type BootstrapPrivacyClassification = 'public' | 'local-sensitive' | 'sec
6767
// @beta
6868
export function computeEnvelopePrivacyFloor(classifications: Iterable<ReporterPrivacyClassification>): ReporterPrivacyClassification;
6969

70+
// @beta
71+
export function computeWallTimeRegressionPercent(baselineMs: number, candidateMs: number): number;
72+
7073
// @beta
7174
export const COPILOT_CLI_ENV_VAR: 'COPILOT_CLI';
7275

7376
// @beta
74-
export function createBeforeLogAdapter(hooks: readonly LegacyBeforeLogHook[]): (aggregate: ITelemetryAggregate) => void;
77+
export function createBeforeLogAdapter(hooks: readonly LegacyBeforeLogHook[]): (aggregate: ITelemetryAggregate) => Record<string, unknown>;
7578

7679
// @beta
7780
export function createColorizer(enabled: boolean): IColorizer;
@@ -80,7 +83,7 @@ export function createColorizer(enabled: boolean): IColorizer;
8083
export function createEngineSink(providedSink?: IReporterEventSink): IEngineSinkResolution;
8184

8285
// @beta
83-
export function createPluginApiIncompatibleDiagnostic(manifest: IRushPluginManifest): IRushDiagnostic;
86+
export function createPluginApiIncompatibleDiagnostic(manifest: IRushPluginManifest, rushVersion: string): IRushDiagnostic;
8487

8588
// @beta
8689
export function createRushDiagnostic(code: RushDiagnosticCodes, options?: ICreateRushDiagnosticOptions): IRushDiagnostic;
@@ -94,6 +97,9 @@ export function createScopedReporter(options: ICreateScopedReporterOptions): ISc
9497
// @beta
9598
export function createTelemetryReporter(subscriber: TelemetrySubscriber): IReporter;
9699

100+
// @beta
101+
export const DAEMON_ALIGNED_MAJOR_REPORTER_DEFAULTS: IReporterMajorDefaults;
102+
97103
// @beta
98104
export const DEFAULT_FLUSH_TIMEOUT_MS: number;
99105

@@ -133,6 +139,9 @@ export function detectAgent(env: Record<string, string | undefined>, configuredV
133139
// @beta
134140
export function encodeNdjsonRecord(value: unknown, options?: INdjsonOptions): string;
135141

142+
// @beta
143+
export function evaluatePluginApplyGate(manifests: readonly IRushPluginManifest[], options: IPluginApplyGateOptions): IPluginApplyDecision[];
144+
136145
// @beta
137146
export const EXIT_CODE_FAILURE: 1;
138147

@@ -161,6 +170,9 @@ export class FileReporter implements IReporter {
161170
// @beta
162171
export function filterEventsForLogLevel(logLevel: ReporterLogLevel, events: readonly IReporterEventEnvelope<unknown>[]): IReporterEventEnvelope<unknown>[];
163172

173+
// @beta
174+
export function getBlockedPlugins(decisions: readonly IPluginApplyDecision[]): IPluginApplyDecision[];
175+
164176
// @beta
165177
export function getEventMinimumLogLevel(event: IReporterEventEnvelope<unknown>): ReporterLogLevel;
166178

@@ -170,6 +182,9 @@ export function getLogLevelRank(level: ReporterLogLevel): number;
170182
// @beta
171183
export function getPrivacyClassificationRank(classification: ReporterPrivacyClassification): number;
172184

185+
// @beta
186+
export function getReporterMigrationPhase(id: ReporterMigrationPhaseId): IReporterMigrationPhase;
187+
173188
// @beta
174189
export function getSignalExitCode(signal: NodeJS.Signals): number;
175190

@@ -273,6 +288,13 @@ export interface IAutomaticReporterPlan {
273288
readonly stdoutOwner: 'machine' | 'human';
274289
}
275290

291+
// @beta
292+
export interface IAutomaticSelectionContext {
293+
readonly emergencyLegacyFallback?: boolean;
294+
readonly experimentalSettingEnabled?: boolean;
295+
readonly explicitOptIn?: boolean;
296+
}
297+
276298
// @beta
277299
export interface IBootstrapEventBufferOptions {
278300
readonly maxBytes?: number;
@@ -615,6 +637,19 @@ export interface IPlaintextReporterOptions {
615637
readonly write: (text: string) => void;
616638
}
617639

640+
// @beta
641+
export interface IPluginApplyDecision {
642+
readonly allowed: boolean;
643+
readonly diagnostic?: IRushDiagnostic;
644+
readonly manifest: IRushPluginManifest;
645+
}
646+
647+
// @beta
648+
export interface IPluginApplyGateOptions {
649+
readonly gateEnabled?: boolean;
650+
readonly rushVersion: string;
651+
}
652+
618653
// @beta
619654
export interface IProblemMatch {
620655
readonly code?: string;
@@ -772,6 +807,18 @@ export interface IReporterHostOptions {
772807
readonly retentionMs?: number;
773808
}
774809

810+
// @beta
811+
export interface IReporterMajorDefaults {
812+
readonly automaticSelectionEnabledByDefault: boolean;
813+
readonly emergencyFallbackEnvVar: string;
814+
readonly emergencyFallbackReporterName: string;
815+
readonly gateIncompatiblePluginsBeforeApply: boolean;
816+
readonly legacyRendererRetained: boolean;
817+
readonly removedTerminalApis: readonly string[];
818+
readonly sentinelBridgeRetained: boolean;
819+
readonly verbosityAliasesRetained: boolean;
820+
}
821+
775822
// @beta
776823
export interface IReporterManagerOptions {
777824
readonly coalesceThreshold?: number;
@@ -780,6 +827,16 @@ export interface IReporterManagerOptions {
780827
readonly protocolVersion?: IReporterProtocolVersion;
781828
}
782829

830+
// @beta
831+
export interface IReporterMigrationPhase {
832+
readonly id: ReporterMigrationPhaseId;
833+
readonly independentlyReleasable: boolean;
834+
readonly ordinal: number;
835+
readonly revertible: boolean;
836+
readonly summary: string;
837+
readonly title: string;
838+
}
839+
783840
// @beta
784841
export interface IReporterOutputTarget {
785842
readonly params: {
@@ -789,6 +846,15 @@ export interface IReporterOutputTarget {
789846
readonly target: string;
790847
}
791848

849+
// @beta
850+
export interface IReporterPerformanceBudgets {
851+
readonly maxAdditionalPeakMemoryBytes: number;
852+
readonly maxAiDetailedDiagnostics: number;
853+
readonly maxAiOutputBytes: number;
854+
readonly maxInteractiveRefreshHz: number;
855+
readonly maxWallTimeRegressionPercent: number;
856+
}
857+
792858
// @beta
793859
export interface IReporterPlanEntry {
794860
readonly destination: string;
@@ -901,8 +967,8 @@ export interface IRushFileDiagnosticSource {
901967

902968
// @beta
903969
export interface IRushPluginManifest {
904-
readonly pluginApiVersion: string;
905970
readonly pluginName: string;
971+
readonly rushVersionRange: string;
906972
}
907973

908974
// @beta
@@ -933,6 +999,9 @@ export function isAgentVariableActive(value: string | undefined): boolean;
933999
// @beta
9341000
export function isAlreadyReportedSentinel(error: unknown): boolean;
9351001

1002+
// @beta
1003+
export function isAutomaticSelectionEnabled(defaults: IReporterMajorDefaults, context?: IAutomaticSelectionContext): boolean;
1004+
9361005
// @beta
9371006
export function isBootstrapHandoffFileName(fileName: string): boolean;
9381007

@@ -961,6 +1030,9 @@ export interface IScopedReporter {
9611030
emitMessage(options: IScopedMessageOptions): string;
9621031
}
9631032

1033+
// @beta
1034+
export function isEmergencyLegacyFallback(env: Record<string, string | undefined>, defaults?: IReporterMajorDefaults): boolean;
1035+
9641036
// @beta
9651037
export interface ISessionCompletedPayload {
9661038
readonly durationMs?: number;
@@ -989,9 +1061,6 @@ export function isLegacyEmergencyFallbackRequested(env: Record<string, string |
9891061
// @beta
9901062
export function isMachineReporter(reporter: ReporterName): boolean;
9911063

992-
// @beta
993-
export function isPluginApiVersionSupported(declaredApiVersion: string, supportedApiVersion?: string): boolean;
994-
9951064
// @beta
9961065
export function isReporterEventRequired(type: ReporterEventType): boolean;
9971066

@@ -1001,15 +1070,27 @@ export function isReporterExtensionEventName(name: string): name is ReporterExte
10011070
// @beta
10021071
export function isReporterProtocolCompatible(consumer: IReporterProtocolVersion, producer: IReporterProtocolVersion): boolean;
10031072

1073+
// @beta
1074+
export function isRushVersionSupported(rushVersionRange: string, rushVersion: string): boolean;
1075+
10041076
// @beta
10051077
export function isSupportedLogLevel(level: string): level is ReporterLogLevel;
10061078

10071079
// @beta
10081080
export function isSupportedReporterName(name: string): name is ReporterName;
10091081

1082+
// @beta
1083+
export function isTerminalApiRemoved(api: string, defaults?: IReporterMajorDefaults): boolean;
1084+
10101085
// @beta
10111086
export function isValidRushDiagnosticCode(code: string): boolean;
10121087

1088+
// @beta
1089+
export function isWithinMemoryBudget(additionalPeakBytes: number, budgets?: IReporterPerformanceBudgets): boolean;
1090+
1091+
// @beta
1092+
export function isWithinWallTimeBudget(baselineMs: number, candidateMs: number, budgets?: IReporterPerformanceBudgets): boolean;
1093+
10131094
// @beta
10141095
export interface ITelemetryAggregate {
10151096
readonly commandName?: string;
@@ -1165,7 +1246,7 @@ export class OldEngineOutputAdapter {
11651246
export type OneOrMoreRushDiagnosticCodeSegments<TSegments extends string = string> = string extends TSegments ? `_${Uppercase<string>}` : TSegments extends `_${infer Segments}` ? Segments extends '' ? never : TSegments extends Uppercase<TSegments> ? TSegments : never : never;
11661247

11671248
// @beta
1168-
export type OperationStatus = 'ready' | 'executing' | 'success' | 'successWithWarnings' | 'failure' | 'blocked' | 'skipped' | 'fromCache' | 'noOp';
1249+
export type OperationStatus = 'ready' | 'waiting' | 'queued' | 'executing' | 'success' | 'successWithWarnings' | 'failure' | 'blocked' | 'skipped' | 'fromCache' | 'noOp' | 'aborted';
11691250

11701251
// @beta
11711252
export class OperationStreamEmitter {
@@ -1212,6 +1293,9 @@ export type PlaintextVariant = 'detailed' | 'concise';
12121293
// @beta
12131294
export function planAutomaticReporters(selection: IReporterSelection): IAutomaticReporterPlan;
12141295

1296+
// @beta
1297+
export const PRE_FLIP_REPORTER_DEFAULTS: IReporterMajorDefaults;
1298+
12151299
// @beta
12161300
export class ProblemMatcherRegistry {
12171301
getMatchers(tool: string, options?: IGetMatchersOptions): IProblemMatcher[];
@@ -1234,6 +1318,9 @@ export function regroupOperationOutput(events: readonly IReporterEventEnvelope<u
12341318
// @beta
12351319
export function relayHeftChildOutput(child: IHeftChildOutputStreams, targets?: IHeftChildOutputTargets): void;
12361320

1321+
// @beta
1322+
export const REMOVED_TERMINAL_APIS: readonly string[];
1323+
12371324
// @beta
12381325
export function renderActiveProjectsRow(projects: readonly string[], width: number): string;
12391326

@@ -1246,9 +1333,15 @@ export const REPORTER_EVENT_TYPES: readonly ["sessionStarted", "sessionCompleted
12461333
// @beta
12471334
export const REPORTER_KNOWN_CAPABILITIES: readonly [];
12481335

1336+
// @beta
1337+
export const REPORTER_MIGRATION_PHASES: readonly IReporterMigrationPhase[];
1338+
12491339
// @beta
12501340
export const REPORTER_PACKAGE_NAME: '@rushstack/rush-reporter';
12511341

1342+
// @beta
1343+
export const REPORTER_PERFORMANCE_BUDGETS: IReporterPerformanceBudgets;
1344+
12521345
// @beta
12531346
export const REPORTER_PROTOCOL_LIMITS: IReporterProtocolLimits;
12541347

@@ -1296,6 +1389,7 @@ export class ReporterManager implements IReporterEventSink {
12961389
closeAsync(timeoutMs?: number): Promise<void>;
12971390
emit<TPayload>(event: IReporterEmitEventInput<TPayload>): string;
12981391
flushAsync(timeoutMs?: number): Promise<void>;
1392+
getPendingEventCount(): number;
12991393
ingestForeignEnvelope(envelope: IReporterEventEnvelope<unknown>): string;
13001394
initializeAsync(): Promise<void>;
13011395
signalFlushAsync(timeoutMs?: number): Promise<void>;
@@ -1304,6 +1398,9 @@ export class ReporterManager implements IReporterEventSink {
13041398
// @beta
13051399
export type ReporterMessageSeverity = 'debug' | 'info' | 'warning' | 'error';
13061400

1401+
// @beta
1402+
export type ReporterMigrationPhaseId = 'contractsAndBaselines' | 'bootstrapAndCompatAdapters' | 'shadowStructuredEmission' | 'optInReporters' | 'heftProtocolTrack' | 'daemonAlignedMajorFlip' | 'laterCleanupMajor';
1403+
13071404
// @beta
13081405
export class ReporterMultiplexer implements IReporter {
13091406
constructor(name: string, reporters: readonly IReporter[]);
@@ -1423,9 +1520,6 @@ export const RUSH_INTERNAL_ERROR_CODE: 'RUSH_INTERNAL_UNEXPECTED';
14231520
// @beta
14241521
export const RUSH_LOGS_DIR_NAME: 'rush-logs';
14251522

1426-
// @beta
1427-
export const RUSH_PLUGIN_API_VERSION: '1.0.0';
1428-
14291523
// @beta
14301524
export const RUSH_REPORTER_BOOTSTRAP_HANDOFF_ENV_VAR: '_RUSH_REPORTER_BOOTSTRAP_HANDOFF';
14311525

libraries/reporter/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
"devDependencies": {
5050
"@rushstack/heft": "workspace:*",
5151
"eslint": "~9.37.0",
52-
"local-node-rig": "workspace:*"
52+
"local-node-rig": "workspace:*",
53+
"@types/semver": "7.7.1"
5354
},
5455
"peerDependencies": {
5556
"@types/node": "*"
@@ -59,5 +60,8 @@
5960
"optional": true
6061
}
6162
},
62-
"sideEffects": false
63+
"sideEffects": false,
64+
"dependencies": {
65+
"semver": "~7.7.4"
66+
}
6367
}

libraries/reporter/src/diagnostics/templates/configuration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
export const CONFIGURATION_DIAGNOSTIC_TEMPLATES = {
1313
'diagnostic.RUSH_CONFIG_INVALID_JSON.summary': 'The configuration file {file} contains invalid JSON.',
1414
'diagnostic.RUSH_PLUGIN_API_INCOMPATIBLE.summary':
15-
'The plugin {pluginName} declares plugin API version {declaredApiVersion}, which is incompatible with this Rush (supported: {supportedApiVersion}).'
15+
'The plugin {pluginName} supports Rush {rushVersionRange}, which does not include the running Rush version {rushVersion}.'
1616
} as const;

0 commit comments

Comments
 (0)