From acdb03395356eacfbab00a210591a4383756799a Mon Sep 17 00:00:00 2001 From: Edward Gou Date: Fri, 13 Nov 2026 10:15:58 -0500 Subject: [PATCH 1/2] ref(dashboards): Use the argument-less trace metrics score formulas Refs ENG-9034 --- static/app/utils/discover/fieldRenderers.tsx | 63 ++++++++++++------- static/app/utils/discover/getSortField.tsx | 2 + .../utils/webVitalTraceMetricFields.ts | 6 +- .../utils/hasUnresolvedTraceMetric.spec.tsx | 4 +- 4 files changed, 47 insertions(+), 28 deletions(-) diff --git a/static/app/utils/discover/fieldRenderers.tsx b/static/app/utils/discover/fieldRenderers.tsx index a6defe3ce0f1..204bbcd37612 100644 --- a/static/app/utils/discover/fieldRenderers.tsx +++ b/static/app/utils/discover/fieldRenderers.tsx @@ -394,6 +394,36 @@ function renderAIOutputMessages(rawOutputMessages: unknown) { * "Special fields" either do not map 1:1 to an single column in the event database, * or they require custom UI formatting that can't be handled by the datatype formatters. */ +function performanceScoreRenderer(field: string): SpecialField { + return { + renderFunc: data => { + const score = data[field]; + if (typeof score !== 'number') { + return {emptyValue}; + } + return ( + + + + ); + }, + }; +} + +function opportunityScoreRenderer(field: string): SpecialField { + return { + renderFunc: data => { + const score = data[field]; + if (typeof score !== 'number') { + return {emptyValue}; + } + return ( + {Math.round(score * 10000) / 100} + ); + }, + }; +} + const SPECIAL_FIELDS: Record = { // This is a custom renderer for a field outside discover // TODO - refactor code and remove from this file or add ability to query for attachments in Discover @@ -866,30 +896,15 @@ const SPECIAL_FIELDS: Record = { ), }, - 'performance_score(measurements.score.total)': { - renderFunc: data => { - const score = data['performance_score(measurements.score.total)']; - if (typeof score !== 'number') { - return {emptyValue}; - } - return ( - - - - ); - }, - }, - 'opportunity_score(measurements.score.total)': { - renderFunc: data => { - const score = data['opportunity_score(measurements.score.total)']; - if (typeof score !== 'number') { - return {emptyValue}; - } - return ( - {Math.round(score * 10000) / 100} - ); - }, - }, + // The spans form takes the total score measurement, the trace metrics form no argument + 'performance_score(measurements.score.total)': performanceScoreRenderer( + 'performance_score(measurements.score.total)' + ), + 'performance_score()': performanceScoreRenderer('performance_score()'), + 'opportunity_score(measurements.score.total)': opportunityScoreRenderer( + 'opportunity_score(measurements.score.total)' + ), + 'opportunity_score()': opportunityScoreRenderer('opportunity_score()'), 'browser.name': { renderFunc: data => { const browserName = data['browser.name']; diff --git a/static/app/utils/discover/getSortField.tsx b/static/app/utils/discover/getSortField.tsx index ab0a6ff81d82..b34a4320b6cd 100644 --- a/static/app/utils/discover/getSortField.tsx +++ b/static/app/utils/discover/getSortField.tsx @@ -41,8 +41,10 @@ const SPECIAL_FIELD_SORT_FIELDS = { 'span.status_code': 'span.status_code', 'performance_score(measurements.score.total)': 'performance_score(measurements.score.total)', + 'performance_score()': 'performance_score()', 'opportunity_score(measurements.score.total)': 'opportunity_score(measurements.score.total)', + 'opportunity_score()': 'opportunity_score()', 'browser.name': 'browser.name', browser: 'browser', 'os.name': 'os.name', diff --git a/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts b/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts index 9d82d9517070..81cb11a5d49a 100644 --- a/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts +++ b/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts @@ -63,8 +63,10 @@ export function webVitalCountField(vital: WebVitals): string { export const WEB_VITAL_SCORE_FIELDS = ORDER.map(webVitalScoreField); export const WEB_VITAL_SCORE_COUNT_FIELDS = ORDER.map(webVitalScoreCountField); -export const PERFORMANCE_SCORE_FIELD = 'performance_score(measurements.score.total)'; -export const OPPORTUNITY_SCORE_FIELD = 'opportunity_score(measurements.score.total)'; +// The trace metrics score formulas read the web vital score metrics directly, so they +// take no argument. The spans dataset versions still take a per-vital measurement. +export const PERFORMANCE_SCORE_FIELD = 'performance_score()'; +export const OPPORTUNITY_SCORE_FIELD = 'opportunity_score()'; export function isWebVitalScoreFormula(field: string): boolean { return field === PERFORMANCE_SCORE_FIELD || field === OPPORTUNITY_SCORE_FIELD; diff --git a/static/app/views/dashboards/widgetBuilder/utils/hasUnresolvedTraceMetric.spec.tsx b/static/app/views/dashboards/widgetBuilder/utils/hasUnresolvedTraceMetric.spec.tsx index 1535de9b13ce..cf8f0a5d709f 100644 --- a/static/app/views/dashboards/widgetBuilder/utils/hasUnresolvedTraceMetric.spec.tsx +++ b/static/app/views/dashboards/widgetBuilder/utils/hasUnresolvedTraceMetric.spec.tsx @@ -52,8 +52,8 @@ describe('hasUnresolvedTraceMetric', () => { WidgetQueryFixture({ aggregates: [ 'count(value,browser.web_vital.ttfb,distribution,millisecond)', - 'performance_score(measurements.score.total)', - 'opportunity_score(measurements.score.total)', + 'performance_score()', + 'opportunity_score()', ], }), ], From 35f8a825d8694ff9ff4b65506736a3e27ad55eef Mon Sep 17 00:00:00 2001 From: Edward Gou Date: Fri, 13 Nov 2026 10:34:43 -0500 Subject: [PATCH 2/2] ref(dashboards): Drop the comment on the score field constants --- static/app/views/dashboards/utils/webVitalTraceMetricFields.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts b/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts index 81cb11a5d49a..4e0a557d6613 100644 --- a/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts +++ b/static/app/views/dashboards/utils/webVitalTraceMetricFields.ts @@ -63,8 +63,6 @@ export function webVitalCountField(vital: WebVitals): string { export const WEB_VITAL_SCORE_FIELDS = ORDER.map(webVitalScoreField); export const WEB_VITAL_SCORE_COUNT_FIELDS = ORDER.map(webVitalScoreCountField); -// The trace metrics score formulas read the web vital score metrics directly, so they -// take no argument. The spans dataset versions still take a per-vital measurement. export const PERFORMANCE_SCORE_FIELD = 'performance_score()'; export const OPPORTUNITY_SCORE_FIELD = 'opportunity_score()';