Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/reporter",
"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",
"type": "minor"
}
],
"packageName": "@rushstack/reporter",
"email": "TheLarkInn@users.noreply.github.com"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "@rushstack/reporter",
"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",
"type": "minor"
}
],
"packageName": "@rushstack/reporter",
"email": "TheLarkInn@users.noreply.github.com"
}
97 changes: 97 additions & 0 deletions common/reviews/api/reporter.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export type BootstrapPrivacyClassification = 'public' | 'local-sensitive' | 'sec
// @beta
export function computeEnvelopePrivacyFloor(classifications: Iterable<ReporterPrivacyClassification>): ReporterPrivacyClassification;

// @beta
export function computeWallTimeRegressionPercent(baselineMs: number, candidateMs: number): number;

// @beta
export const COPILOT_CLI_ENV_VAR: 'COPILOT_CLI';

Expand Down Expand Up @@ -91,6 +94,9 @@ export function createScopedReporter(options: ICreateScopedReporterOptions): ISc
// @beta
export function createTelemetryReporter(subscriber: TelemetrySubscriber): IReporter;

// @beta
export const DAEMON_ALIGNED_MAJOR_REPORTER_DEFAULTS: IReporterMajorDefaults;

// @beta
export const DEFAULT_FLUSH_TIMEOUT_MS: number;

Expand Down Expand Up @@ -130,6 +136,9 @@ export function detectAgent(env: Record<string, string | undefined>, configuredV
// @beta
export function encodeNdjsonRecord(value: unknown, options?: INdjsonOptions): string;

// @beta
export function evaluatePluginApplyGate(manifests: readonly IRushPluginManifest[], options?: IPluginApplyGateOptions): IPluginApplyDecision[];

// @beta
export const EXIT_CODE_FAILURE: 1;

Expand Down Expand Up @@ -158,6 +167,9 @@ export class FileReporter implements IReporter {
// @beta
export function filterEventsForLogLevel(logLevel: ReporterLogLevel, events: readonly IReporterEventEnvelope<unknown>[]): IReporterEventEnvelope<unknown>[];

// @beta
export function getBlockedPlugins(decisions: readonly IPluginApplyDecision[]): IPluginApplyDecision[];

// @beta
export function getEventMinimumLogLevel(event: IReporterEventEnvelope<unknown>): ReporterLogLevel;

Expand All @@ -167,6 +179,9 @@ export function getLogLevelRank(level: ReporterLogLevel): number;
// @beta
export function getPrivacyClassificationRank(classification: ReporterPrivacyClassification): number;

// @beta
export function getReporterMigrationPhase(id: ReporterMigrationPhaseId): IReporterMigrationPhase;

// @beta
export function getSignalExitCode(signal: NodeJS.Signals): number;

Expand Down Expand Up @@ -265,6 +280,13 @@ export interface IAutomaticReporterPlan {
readonly stdoutOwner: 'machine' | 'human';
}

// @beta
export interface IAutomaticSelectionContext {
readonly emergencyLegacyFallback?: boolean;
readonly experimentalSettingEnabled?: boolean;
readonly explicitOptIn?: boolean;
}

// @beta
export interface IBootstrapEventBufferOptions {
readonly maxBytes?: number;
Expand Down Expand Up @@ -569,6 +591,19 @@ export interface IPlaintextReporterOptions {
readonly write: (text: string) => void;
}

// @beta
export interface IPluginApplyDecision {
readonly allowed: boolean;
readonly diagnostic?: IRushDiagnostic;
readonly manifest: IRushPluginManifest;
}

// @beta
export interface IPluginApplyGateOptions {
readonly gateEnabled?: boolean;
readonly supportedApiVersion?: string;
}

// @beta
export interface IProblemMatch {
readonly code?: string;
Expand Down Expand Up @@ -726,6 +761,18 @@ export interface IReporterHostOptions {
readonly retentionMs?: number;
}

// @beta
export interface IReporterMajorDefaults {
readonly automaticSelectionEnabledByDefault: boolean;
readonly emergencyFallbackEnvVar: string;
readonly emergencyFallbackReporterName: string;
readonly gateIncompatiblePluginsBeforeApply: boolean;
readonly legacyRendererRetained: boolean;
readonly removedTerminalApis: readonly string[];
readonly sentinelBridgeRetained: boolean;
readonly verbosityAliasesRetained: boolean;
}

// @beta
export interface IReporterManagerOptions {
readonly coalesceThreshold?: number;
Expand All @@ -734,6 +781,16 @@ export interface IReporterManagerOptions {
readonly protocolVersion?: IReporterProtocolVersion;
}

// @beta
export interface IReporterMigrationPhase {
readonly id: ReporterMigrationPhaseId;
readonly independentlyReleasable: boolean;
readonly ordinal: number;
readonly revertible: boolean;
readonly summary: string;
readonly title: string;
}

// @beta
export interface IReporterOutputTarget {
readonly params: {
Expand All @@ -743,6 +800,15 @@ export interface IReporterOutputTarget {
readonly target: string;
}

// @beta
export interface IReporterPerformanceBudgets {
readonly maxAdditionalPeakMemoryBytes: number;
readonly maxAiDetailedDiagnostics: number;
readonly maxAiOutputBytes: number;
readonly maxInteractiveRefreshHz: number;
readonly maxWallTimeRegressionPercent: number;
}

// @beta
export interface IReporterPlanEntry {
readonly destination: string;
Expand Down Expand Up @@ -877,6 +943,9 @@ export function isAgentVariableActive(value: string | undefined): boolean;
// @beta
export function isAlreadyReportedSentinel(error: unknown): boolean;

// @beta
export function isAutomaticSelectionEnabled(defaults: IReporterMajorDefaults, context?: IAutomaticSelectionContext): boolean;

// @beta
export function isBootstrapHandoffFileName(fileName: string): boolean;

Expand Down Expand Up @@ -905,6 +974,9 @@ export interface IScopedReporter {
emitMessage(options: IScopedMessageOptions): string;
}

// @beta
export function isEmergencyLegacyFallback(env: Record<string, string | undefined>, defaults?: IReporterMajorDefaults): boolean;

// @beta
export interface ISessionCompletedPayload {
readonly durationMs?: number;
Expand Down Expand Up @@ -948,9 +1020,18 @@ export function isSupportedLogLevel(level: string): level is ReporterLogLevel;
// @beta
export function isSupportedReporterName(name: string): name is ReporterName;

// @beta
export function isTerminalApiRemoved(api: string, defaults?: IReporterMajorDefaults): boolean;

// @beta
export function isValidRushDiagnosticCode(code: string): boolean;

// @beta
export function isWithinMemoryBudget(additionalPeakBytes: number, budgets?: IReporterPerformanceBudgets): boolean;

// @beta
export function isWithinWallTimeBudget(baselineMs: number, candidateMs: number, budgets?: IReporterPerformanceBudgets): boolean;

// @beta
export interface ITelemetryAggregate {
readonly commandName?: string;
Expand Down Expand Up @@ -1133,6 +1214,9 @@ export type PlaintextVariant = 'detailed' | 'concise';
// @beta
export function planAutomaticReporters(selection: IReporterSelection): IAutomaticReporterPlan;

// @beta
export const PRE_FLIP_REPORTER_DEFAULTS: IReporterMajorDefaults;

// @beta
export class ProblemMatcherRegistry {
getMatchers(tool: string, options?: IGetMatchersOptions): IProblemMatcher[];
Expand All @@ -1148,6 +1232,9 @@ export function readChildDescriptorFd(env: Record<string, string | undefined>):
// @beta
export function regroupOperationOutput(events: readonly IReporterEventEnvelope<unknown>[]): Map<string, string>;

// @beta
export const REMOVED_TERMINAL_APIS: readonly string[];

// @beta
export function renderActiveProjectsRow(projects: readonly string[], width: number): string;

Expand All @@ -1157,9 +1244,15 @@ export function renderLiveRegion(state: ILiveRegionState, options: IRenderLiveRe
// @beta
export const REPORTER_EVENT_TYPES: readonly ReporterEventType[];

// @beta
export const REPORTER_MIGRATION_PHASES: readonly IReporterMigrationPhase[];

// @beta
export const REPORTER_PACKAGE_NAME: '@rushstack/reporter';

// @beta
export const REPORTER_PERFORMANCE_BUDGETS: IReporterPerformanceBudgets;

// @beta
export const REPORTER_PROTOCOL_LIMITS: IReporterProtocolLimits;

Expand Down Expand Up @@ -1202,6 +1295,7 @@ export class ReporterManager implements IReporterEventSink {
closeAsync(timeoutMs?: number): Promise<void>;
emit<TPayload>(event: IReporterEmitEventInput<TPayload>): string;
flushAsync(timeoutMs?: number): Promise<void>;
getPendingEventCount(): number;
ingestForeignEnvelope(envelope: IReporterEventEnvelope<unknown>): string;
initializeAsync(): Promise<void>;
signalFlushAsync(timeoutMs?: number): Promise<void>;
Expand All @@ -1210,6 +1304,9 @@ export class ReporterManager implements IReporterEventSink {
// @beta
export type ReporterMessageSeverity = 'debug' | 'info' | 'warning' | 'error';

// @beta
export type ReporterMigrationPhaseId = 'contractsAndBaselines' | 'bootstrapAndCompatAdapters' | 'shadowStructuredEmission' | 'optInReporters' | 'heftProtocolTrack' | 'daemonAlignedMajorFlip' | 'laterCleanupMajor';

// @beta
export class ReporterMultiplexer implements IReporter {
constructor(name: string, reporters: readonly IReporter[]);
Expand Down
25 changes: 25 additions & 0 deletions libraries/reporter/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,28 @@ export type {
} from './producers/IScopedReporter';
export type { ReporterExtensionEventName } from './producers/ReporterExtensionEventName';
export { isReporterExtensionEventName } from './producers/ReporterExtensionEventName';

export type { IReporterPerformanceBudgets } from './perf/PerformanceBudgets';
export {
REPORTER_PERFORMANCE_BUDGETS,
computeWallTimeRegressionPercent,
isWithinWallTimeBudget,
isWithinMemoryBudget
} from './perf/PerformanceBudgets';

export type { ReporterMigrationPhaseId, IReporterMigrationPhase } from './migration/MigrationPhase';
export { REPORTER_MIGRATION_PHASES, getReporterMigrationPhase } from './migration/MigrationPhase';
export type {
IReporterMajorDefaults,
IAutomaticSelectionContext
} from './migration/DaemonAlignedMajorDefaults';
export {
REMOVED_TERMINAL_APIS,
PRE_FLIP_REPORTER_DEFAULTS,
DAEMON_ALIGNED_MAJOR_REPORTER_DEFAULTS,
isTerminalApiRemoved,
isEmergencyLegacyFallback,
isAutomaticSelectionEnabled
} from './migration/DaemonAlignedMajorDefaults';
export type { IPluginApplyGateOptions, IPluginApplyDecision } from './migration/PluginApplyGate';
export { evaluatePluginApplyGate, getBlockedPlugins } from './migration/PluginApplyGate';
18 changes: 18 additions & 0 deletions libraries/reporter/src/manager/ReporterManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,24 @@ export class ReporterManager implements IReporterEventSink {
return rehomed.eventId;
}

/**
* Returns the total number of envelopes still buffered across all reporter
* queues.
*
* @remarks
* This is an observability hook for verifying bounded streaming: because each
* queue drains incrementally and coalesces replaceable status events, the
* pending count stays bounded rather than growing to the whole-build event
* total. After {@link ReporterManager.flushAsync} resolves it is `0`.
*/
public getPendingEventCount(): number {
let total: number = 0;
for (const entry of this._entries) {
total += entry.queue.length;
}
return total;
}

/**
* Drains every reporter queue and flushes each reporter, bounded by a timeout.
*
Expand Down
Loading