Skip to content

Replays Self-Serve Bulk Delete System#11

Open
akshayutture-augment wants to merge 32 commits into
replays-delete-vulnerablefrom
replays-delete-stable
Open

Replays Self-Serve Bulk Delete System#11
akshayutture-augment wants to merge 32 commits into
replays-delete-vulnerablefrom
replays-delete-stable

Conversation

@akshayutture-augment

@akshayutture-augment akshayutture-augment commented Nov 14, 2025

Copy link
Copy Markdown

Test 5

Summary by CodeRabbit

Release Notes

  • New Features

    • Added AI-powered feedback summaries in the feedback list.
    • Introduced table widget visualization for dashboards with customizable columns.
    • Added owner assignment support for workflow detectors.
    • Enhanced replay summaries with error context integration.
  • Bug Fixes

    • Improved PR comment formatting with better issue title and environment display.
    • Refined feedback summary generation with stricter length and format constraints.
    • Disabled autofix automation tuning by default for improved stability.

Replicated from ai-code-review-evaluation/sentry-coderabbit#5

armenzg and others added 30 commits June 20, 2025 12:49
…o 'low' (#93927)"

This reverts commit 8d04522.

Co-authored-by: roaga <47861399+roaga@users.noreply.github.com>
Missed in the initial commit, leading to some relevant logs being
unannotated.
We have had a few tasks get killed at 10% rollout.
Also add a test, so that this doesn't happen again
Fixes DE-129 and DE-156

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
These transitions should be matching
…` (#93946)

Use `project_id` on the replay record instead of the URL (where it does
not always exist).

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
Also fixed `replay.view_html` -> `replay.view-html`

---------

Co-authored-by: Michelle Zhang <56095982+michellewzhang@users.noreply.github.com>
…948)

gets `npx @typescript/native-preview` passing again
The conditions associated with a DCG can change over time, and it's good
if we can be completely confident that they're consistent within a given
task execution.
This is unused and most regex experiments have required broader changes
to ensure that regexes are evaluated in a specific order (ex:
traceparent). Removing this for now to simplify the code and very
slightly improve runtime performance.
From some testing (on feedback lists of all different lengths), this
prompt seems to work better. It doesn't write overly long sentences and
also does a better job at "summarizing" versus just mentioning a few
specific topics and leaving out others.
Just remove a couple custom Flex* classes in favor of the Flex primitive
This has been killed a few times.

Refs SENTRY-42M7
…n table (#93892)

<!-- Describe your PR here. -->

[ticket](https://linear.app/getsentry/issue/ID-156/grouping-info-remove-type-field-from-ui)
The Type field in the Grouping Info section of the issue details page
was redundant.
This removes the Type row from all variant types while keeping the
underlying data structure intact.

before
![Screenshot 2025-06-20 at 12 00
54 PM](https://github.com/user-attachments/assets/97ca72da-0a52-4446-9825-cd4fcb505adf)

after
![Screenshot 2025-06-20 at 11 59
29 AM](https://github.com/user-attachments/assets/a4284d2b-c9f5-442f-b010-7fe72a598e39)
### Changes
Related to this PR: getsentry/sentry#93810. This
is part 1 of the change, which is pulling out the new component and just
adding it to the repo. Also includes some simplification of the logic in
the base component.

Part 2 will be replacing tables in widgets.

### Before/After

There is no UI change as the table is not being used yet. There is a new
story page for the component.
…93943)

to prevent this issue from becoming too noisy, add a noise config
Unfortunately, 'event_data' went from being the variable for current
event context to being the complete parsed data from Redis, and we
continued logging it per group.
That's more data than we should be logging even arguably once, let alone
per group.
Co-authored-by: Abdullah Khan <abdullahkhan@PG9Y57YDXQ.local>
Adds some simple analytics to our endpoint so we can begin building a
dashboard in Amplitude.
Previously, explore supported multiple y axis per chart, so each
visualize supported multiple y axis. That functionality has since been
removed for simplicity so update the types here to match. Keep in mind
that saved queries still store them as an array so when
serializing/deserializing, we still need to treat it as an array.
We'll need the `useGetTraceItemAttributeKeys` hook in other places so
refactoring it so that it can exported.
mrduncan and others added 2 commits June 20, 2025 13:20
When the max segment ID is null the process fails. We should exit early
since if there aren't any segments to delete there's nothing to do.
@akshayutture-augment

Copy link
Copy Markdown
Author

@coderabbitai review

@akshayutture-augment

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 18, 2025

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Nov 18, 2025

Copy link
Copy Markdown

Walkthrough

This pull request introduces service configuration updates, refactors the Visualize API from array-based to single-axis, enhances feedback summarization with AI features, updates PR comment formatting across integrations, adds error context to replay breadcrumbs, and modernizes UI layout components by replacing custom wrappers with a shared Flex component. Additionally, it removes deprecated grouping experiments, updates automation tuning defaults, and extends workflow detector capabilities with owner management.

Changes

Cohort / File(s) Summary
Service Configuration & Dependencies
devservices/config.yml
Added snuba-metrics service dependency and four subscription-results consumers (eap-spans, subscription-results-eap-items, metrics, generic-metrics) to tracing and program definitions.
Constants & Defaults
src/sentry/constants.py, src/sentry/projectoptions/defaults.py, src/sentry/options/defaults.py
Changed DEFAULT_AUTOFIX_AUTOMATION_TUNING_DEFAULT and sentry:autofix_automation_tuning from "low" to "off"; removed grouping.experiments.parameterization.traceparent option registration.
Feedback & AI Summaries
src/sentry/feedback/usecases/feedback_summaries.py, static/app/components/feedback/*
Expanded feedback summary instruction constraints (55 words max, two sentences max, theme frequency tracking); introduced FeedbackSummary component and useFeedbackSummary hook for feature-flagged AI-powered summaries.
Feedback Summary Data Fetching
static/app/components/feedback/list/useFeedbackSummary.tsx, static/app/views/feedback/feedbackListPage.tsx
New hook for API-based feedback summary retrieval with feature flag gating; integrated FeedbackSummary rendering above feedback list.
GitHub/GitLab PR Comment Formatting
src/sentry/integrations/github/integration.py, src/sentry/integrations/gitlab/integration.py, src/sentry/integrations/source_code_management/commit_context.py
Refactored PR comment issue formatting: replaced direct template usage with per-issue title truncation (50 chars max) and environment-aware formatting; extracted template and helpers to base CommitContextIntegration class.
Replay Error Context
src/sentry/replays/endpoints/project_replay_summarize_breadcrumbs.py
Added error context enrichment: fetches error events from replay, merges with breadcrumbs in chronological order, interleaves error logs with segment analysis for Seer-based summarization.
Replay Deletion
src/sentry/replays/usecases/delete.py
Added None-handling for max_segment_id in MatchedRow to skip deletion when no segments exist.
Browser Reporting Endpoint
src/sentry/issues/endpoints/browser_reporting_collector.py
Introduced BrowserReportSerializer (DRF-based) and BrowserReportsJSONParser; replaced dataclass validation with DRF serializer; added per-report validation with 422 error responses; updated endpoint to return DRF Response objects.
Issue Grouping
src/sentry/issues/grouptype.py
Added noise_config with ignore_limit=5 to DBQueryInjectionVulnerabilityGroupType for enhanced noise handling.
Grouping Parameterization
src/sentry/grouping/parameterization.py
Removed ParameterizationRegexExperiment class and consolidated all experiments to use uniform run() interface; simplified experiment dispatch logic.
Webhook Task Configuration
src/sentry/hybridcloud/tasks/deliver_webhooks.py
Added processing_deadline_duration to TaskworkerConfig for schedule_webhook_delivery (30s), drain_mailbox (300s), and drain_mailbox_parallel (120s).
Auth Task Configuration
src/sentry/tasks/auth/check_auth.py
Added processing_deadline_duration of 60 seconds to check_auth_identities task.
Preprod Analytics
src/sentry/preprod/__init__.py, src/sentry/preprod/analytics.py, src/sentry/preprod/api/endpoints/organization_preprod_artifact_assemble.py
New analytics event PreprodArtifactApiAssembleEvent for artifact API assemble actions; integrated event recording in endpoint with organization_id, project_id, user_id.
Snuba Query Enhancement
src/sentry/snuba/ourlogs.py
Added optional equations parameter to run_top_events_timeseries_query, forwarded to RPC layer.
Workflow Engine Detectors
src/sentry/workflow_engine/endpoints/validators/base/detector.py
Added owner field (ActorField) to BaseDetectorTypeValidator; implemented owner_user_id/owner_team_id assignment for users and teams.
Workflow Engine Delayed Processing
src/sentry/workflow_engine/processors/delayed_workflow.py
Refactored get_condition_query_groups and get_groups_to_fire to accept precomputed dcg_to_slow_conditions parameter; added logging for no_slow_condition_groups; updated fire_actions_for_groups log references.
Workflow Logger
src/sentry/workflow_engine/processors/workflow.py
Replaced standard logging.getLogger with log_context.get_logger for improved logging context.
Data Migrations
src/sentry/migrations/0917_convert_org_saved_searches_to_views.py, src/sentry/migrations/0920_convert_org_saved_searches_to_views_revised.py
Disabled both SavedSearch-to-GroupSearchView conversion migrations to no-ops due to errors.
Flex Layout Component Refactoring (Extensive)
Multiple static/app/components/*, static/app/views/* files
Replaced 40+ instances of custom FlexCenter, FlexContainer, FlexBox, and FlexDiv wrappers with shared Flex component from layout library across dashboard widgets, alerts, codecov, feedback, performance, profiling, and settings UI components.
Visualize API Refactoring (Core)
static/app/views/explore/contexts/pageParamsContext/visualizes.tsx, .../visualizes.spec.tsx, .../index.tsx, .../index.spec.tsx
Major API change: Visualize constructor changed from accepting yAxes array to single yAxis string; updated Visualize.fromJSON to return Visualize[]; propagated single-axis handling throughout explore context.
Visualize Usage Cascading Changes
25+ explore-related files
Updated all usages of Visualize to pass single yAxis string; replaced visualize.yAxes array iteration with visualize.yAxis; removed dedupeArray for multiple axes; simplified y-axis field collection logic. Affected files: aggregateFields, charts/index, components, sortBys, hooks (useAnalytics, useExploreAggregatesTable, useExploreTimeseries, useAddToDashboard, useTopEvents), spans/spansTab, tables (aggregateColumnEditorModal, defaultTableCellRenderers), toolbar/* modules, utils/*.
Explore Trace Item Attributes
static/app/views/explore/hooks/useGetTraceItemAttributeKeys.tsx, useGetTraceItemAttributeValues.tsx, useTraceItemAttributeKeys.tsx, types.tsx
Created new useGetTraceItemAttributeKeys hook and supporting query options builder; renamed useTraceItemAttributeValues to useGetTraceItemAttributeValues; extracted UseTraceItemAttributeBaseProps to shared types; removed attributeKey and enabled parameters from public API.
Dashboard Table Widget
static/app/views/dashboards/widgets/tableWidget/*
Introduced TabularColumn type; created TableWidgetVisualization component with LoadingPlaceholder; added defaultTableCellRenderers (renderDefaultHeadCell, renderDefaultBodyCell); added Storybook stories and test coverage.
Dashboard Widget Types
static/app/views/dashboards/widgets/common/types.tsx
Added TabularColumn public type with key, name, type, and width fields.
Table Widget Fixtures
tests/js/fixtures/tabularColumn.ts, tests/js/fixtures/tabularColumns.ts, static/app/views/dashboards/widgets/tableWidget/fixtures/sampleHTTPRequestTableData.ts
New fixture files for TabularColumn testing and sample HTTP request table data.
Dashboard Integration
static/app/views/dashboards/widgetCard/chart.tsx
Added conditional rendering for TableWidgetVisualization based on use-table-widget-visualization feature flag.
Event Grouping UI
static/app/components/events/groupingInfo/groupingVariant.tsx
Removed Type row rendering for COMPONENT, CUSTOM_FINGERPRINT, BUILT_IN_FINGERPRINT, SALTED_COMPONENT, and PERFORMANCE_PROBLEM variant types.
Trace UI Components
static/app/components/events/interfaces/spans/newTraceDetailsHeader.tsx, static/app/views/performance/newTraceDetails/traceTabsAndVitals.tsx, static/app/views/performance/newTraceDetails/traceWaterfall.tsx, static/app/components/group/times.tsx
Replaced custom layout wrappers (BreakDownRow, FlexBox, Container) with Flex components; added TextOverflow for truncated text display where applicable.
Replay Breadcrumb UI
static/app/components/replays/breadcrumbs/breadcrumbItem.tsx
Renamed analytics event from 'replay.view_html' to 'replay.view-html'.
Replay Timeline
static/app/components/replays/timeAndScrubberGrid.tsx
Added analytics tracking for zoom in/out actions via trackAnalytics; introduced useCallback optimization for zoom handlers.
Analytics Event Tracking
static/app/utils/analytics/replayAnalyticsEvents.tsx
Added new replay analytics events: 'replay.timeline.zoom-in' and 'replay.timeline.zoom-out'.
Code Snippet Component
static/app/components/codeSnippet.tsx
Replaced custom FlexSpacer with Flex.Item grow={1} for layout spacing.
Scroll Carousel
static/app/components/scrollCarousel.tsx
Updated RightMask gradient to use transparent-to-theme gradient when transparentMask is true.
Button Styling
static/app/components/core/button/styles.chonk.tsx
Reduced transform transition duration from 0.1s to 0.06s on pseudo-element.
Chart Action Dropdown
static/app/views/insights/common/components/chartActionDropdown.tsx
Added referrer parameter to ChartActionDropdown props; threaded through to getExploreUrl and getAlertsUrl calls.
Insights Trace Search
static/app/views/insights/pages/transactionNameSearchBar.tsx
Updated to use new useGetTraceItemAttributeValues hook without attributeKey and enabled parameters.
Team Misery
static/app/views/organizationStats/teamInsights/teamMisery.tsx
Replaced FlexCenter wrapper with Flex align="center".
Performance Attributes
static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx
Added HIDDEN_ATTRIBUTES constant ['is_segment', 'project_id', 'received'] for search filtering.
Replays AI Summary
static/app/views/replays/detail/ai/index.tsx
Changed project_id resolution to use replayRecord.project_id directly; added guard for missing project with error alert.
Dynamic Sampling UI
static/app/views/settings/dynamicSampling/organizationSampleRateInput.tsx
Replaced custom FlexRow wrapper with Flex-based SampleRateRow container.
Audit Log UI
static/app/views/settings/organizationAuditLog/auditLogList.tsx
Replaced FlexCenter wrapper with Flex align="center" for event type and IP display.
Integration Details UI
static/app/views/settings/organizationIntegrations/detailedView/integrationLayout.tsx
Replaced FlexContainer with Flex.Item grow={1} for description and feature list.
Code Owner Table UI
static/app/views/settings/project/projectOwnership/codeOwnerFileTable.tsx
Replaced FlexCenter wrappers with Flex align="center" and gap spacing.
OAuth Admin UI
static/gsAdmin/views/instanceLevelOAuth/instanceLevelOAuthDetails.tsx
Replaced FlexDiv wrapper with Flex justify="right" for Delete client section.
Profiling Summary
static/app/views/profiling/profileSummary/index.tsx
Explicitly typed useMemo callback and frame parameters with Frame type annotations.
Grouping Profile
static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx
Added typing constraints for flamegraphFrameFilter callback.
Browser Reporting Tests
tests/sentry/api/endpoints/test_browser_reporting_collector.py
Introduced DEPRECATION_REPORT and INTERVENTION_REPORT constants; added assert_invalid_report_data helper; expanded validation test coverage for missing fields, invalid types, URLs, timestamps, attempts.
Project Details Tests
tests/sentry/api/endpoints/test_project_details.py, tests/sentry/api/serializers/test_project.py
Updated autofix_automation_tuning default expectations from "low" to "off".
Parameterization Tests
tests/sentry/grouping/test_parameterization.py
Removed ParameterizationRegexExperiment test coverage and imports.
GitHub PR Comment Tests
tests/sentry/integrations/github/tasks/test_pr_comment.py, tests/sentry/integrations/gitlab/tasks/test_pr_comment.py
Updated test data and expectations to reflect new per-issue title/environment formatting in PR comment bodies.
Migration Tests
tests/sentry/migrations/test_0917_convert_org_saved_searches_to_views.py
Removed test file for disabled migration.
Replay Tests
tests/sentry/replays/tasks/test_delete_replays_bulk.py, tests/sentry/replays/test_project_replay_summarize_breadcrumbs.py
Updated max_segment_id test data to include None; expanded breadcrumb tests to cover error context handling and interleaved log generation.
Workflow Engine Tests
tests/sentry/workflow_engine/endpoints/test_organization_detector_details.py, test_organization_detector_index.py, tests/sentry/workflow_engine/processors/test_delayed_workflow.py
Added tests for detector owner management (user/team/clear); added dcg_to_slow_conditions parameter passing in delayed workflow tests.
Stats Tests
tests/snuba/api/endpoints/test_organization_events_stats.py
Renamed OrganizationEventsStatsTopNEvents to OrganizationEventsStatsTopNEventsSpans; added new OrganizationEventsStatsTopNEventsLogs test class.

Sequence Diagram(s)

sequenceDiagram
    participant Endpoint as Browser Reporting<br/>Endpoint
    participant Serializer as BrowserReportSerializer
    participant Parser as BrowserReportsJSONParser
    participant Validation as Validation<br/>Pipeline
    participant Metrics as Metrics

    Endpoint->>Parser: Parse request body
    Parser->>Endpoint: Return raw data
    Endpoint->>Validation: Validate report array
    
    rect rgb(200, 220, 255)
    Note over Validation: Per-report validation
    Validation->>Serializer: Serialize each report
    Serializer->>Serializer: Check required fields,<br/>validate type/url/timestamp
    end
    
    alt All valid
        Validation->>Metrics: Increment metrics<br/>per report type
        Metrics->>Endpoint: Success (200)
    else Any invalid
        Validation->>Endpoint: Return 422<br/>with error details
    end
Loading
sequenceDiagram
    participant Replay as Replay Endpoint
    participant DB as Nodestore
    participant Seer as Seer Service
    participant Response

    Replay->>DB: Fetch error IDs from replay
    Replay->>DB: Retrieve error events<br/>(title, message, timestamp)
    
    rect rgb(220, 255, 220)
    Note over Replay: Error Context Integration
    Replay->>Replay: Interleave errors<br/>with breadcrumbs<br/>chronologically
    end
    
    Replay->>Seer: analyze_recording_segments<br/>(error_events, segments)
    Seer->>Seer: Generate summary<br/>from merged logs
    Seer->>Response: Return AI summary

Loading
sequenceDiagram
    participant UI as Explore Chart
    participant Config as Visualize Config
    participant Query as Query Builder
    
    rect rgb(255, 240, 200)
    Note over UI: Old: Array-based yAxes
    UI->>Config: visualize.yAxes = ['expr1', 'expr2']
    Config->>Query: Multiple series per chart
    end
    
    rect rgb(200, 255, 220)
    Note over UI: New: Single yAxis
    UI->>Config: visualize.yAxis = 'expr1'
    Config->>Query: Single series per chart
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • Visualize API refactoring (static/app/views/explore/contexts/pageParamsContext/visualizes.tsx and 25+ dependent files): The pervasive change from array-based yAxes to single yAxis affects field collection, deduplication logic, and query building throughout the explore module. Verify all call sites properly handle singular values and that no logic errors were introduced in the conversion.
  • Replay error context implementation (src/sentry/replays/endpoints/project_replay_summarize_breadcrumbs.py): Complex interleaving of error events with breadcrumbs and segments requires careful verification of chronological ordering, error fetching error handling, and integration with Seer-based summarization.
  • GitHub/GitLab PR comment formatting (src/sentry/integrations/github/integration.py, src/sentry/integrations/gitlab/integration.py, src/sentry/integrations/source_code_management/commit_context.py): Title truncation logic, environment extraction, and per-issue template formatting need validation; verify the methods get_environment_info, get_merged_pr_single_issue_template, and _truncate_title work correctly across both integrations.
  • Workflow engine owner and slow conditions (src/sentry/workflow_engine/endpoints/validators/base/detector.py and processors/delayed_workflow.py): Verify owner_user_id/owner_team_id assignment logic, actor field validation, and the threading of precomputed dcg_to_slow_conditions through the processing pipeline.
  • BrowserReportSerializer and validation (src/sentry/issues/endpoints/browser_reporting_collector.py): DRF serializer integration, per-report validation, and error response formatting are critical for API stability; verify status codes (422 vs. 404) and error detail structure match client expectations.
  • Migration disabling (src/sentry/migrations/0917_*, 0920_*): Verify that data loss is acceptable and no alternative migration path exists; confirm no deployed instances rely on these migrations running.

🐰 With whiskers twitching and ears perked high,
Visualize hops from arrays to sky,
Error logs dance with breadcrumbs so fine,
And PR comments format in perfect design!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Replays Self-Serve Bulk Delete System' is not clearly related to the actual changeset, which includes dozens of unrelated modifications across multiple systems (feedback summaries, autofix tuning, GitHub/GitLab integrations, browser reporting, workflow engines, explore visualizations, and more). Update the PR title to accurately reflect the primary changes in the changeset. Consider a more comprehensive title like 'Multiple System Updates: Feedback AI, Autofix Defaults, Integration Refactoring, Visualizations, and Migrations' or split into focused PRs by feature area.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch replays-delete-stable

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx (1)

59-70: Inconsistent filtering: hidden attributes only filtered during search.

The current logic filters out hidden attributes only when a search query is present (lines 65-69), but returns all attributes including hidden ones when there's no search query (lines 61-63). This means 'is_segment', 'project_id', and 'received' will be visible to users until they start searching, which is likely unintended.

Apply this diff to filter hidden attributes consistently regardless of search state:

  const sortedAndFilteredAttributes = useMemo(() => {
-    const sorted = sortAttributes(attributes);
+    const sorted = sortAttributes(attributes).filter(
+      attribute => !HIDDEN_ATTRIBUTES.includes(attribute.name)
+    );
    if (!searchQuery.trim()) {
      return sorted;
    }

    return sorted.filter(
      attribute =>
-        !HIDDEN_ATTRIBUTES.includes(attribute.name) &&
        attribute.name.toLowerCase().trim().includes(searchQuery.toLowerCase().trim())
    );
  }, [attributes, searchQuery]);
static/app/views/settings/organizationAuditLog/auditLogList.tsx (1)

437-440: Fix invalid white-space value to avoid unintended wrapping

white-space: no-wrap; is not a valid CSS value and will be ignored by browsers, so MonoDetail text may wrap unexpectedly. This is likely meant to prevent wrapping.

You can correct this with:

-const MonoDetail = styled('code')`
-  font-size: ${p => p.theme.fontSizeMedium};
-  white-space: no-wrap;
-`;
+const MonoDetail = styled('code')`
+  font-size: ${p => p.theme.fontSizeMedium};
+  white-space: nowrap;
+`;
static/app/views/explore/hooks/useTopEvents.tsx (1)

19-21: Update the outdated comment.

The comment mentions "when there are no multiple y-axes chart," but the code no longer checks for multiple y-axes since the multi-yAxis logic was removed. This could confuse future developers.

Apply this diff to update the comment:

-    // We only support top events in aggregates mode for
-    // when there are no multiple y-axes chart and there is at least one group by.
+    // We only support top events in aggregates mode when there is at least one group by.
🧹 Nitpick comments (37)
static/app/views/settings/project/projectOwnership/codeOwnerFileTable.tsx (1)

110-165: Layout refactor to Flex is sound; consider deduplicating repeated props

Wrapping each cell’s content in <Flex align="center" gap={space(1)}> keeps table semantics intact and improves alignment; behavior should remain unchanged.

You could optionally reduce duplication by extracting a small wrapper, e.g.:

+const TableCellFlex = (props: React.PropsWithChildren) => (
+  <Flex align="center" gap={space(1)} {...props} />
+);

and then using <TableCellFlex> in place of each repeated <Flex align="center" gap={space(1)}> instance.

src/sentry/migrations/0920_convert_org_saved_searches_to_views_revised.py (1)

13-15: No‑op migration body looks correct and safe

Turning convert_org_saved_searches_to_views into a documented no‑op is a reasonable way to keep the migration in the chain while avoiding the faulty data logic, given that this migration was never run and a corrected version lives in 0921_convert_org_saved_searches_to_views_rerevised.py. No further changes needed here.

static/app/views/profiling/profileSummary/index.tsx (2)

338-346: Explicit typing improves type safety.

The addition of explicit return type annotation and parameter types is a solid improvement for type safety and maintainability.

For complete consistency, consider adding explicit parameter typing to line 340 as well:

Apply this diff for consistency:

   const flamegraphFrameFilter = useMemo((): ((frame: Frame) => boolean) => {
     if (frameFilter === 'all') {
-      return () => true;
+      return (frame: Frame) => true;
     }
     if (frameFilter === 'application') {
       return (frame: Frame) => frame.is_application;
     }
     return (frame: Frame) => !frame.is_application;
   }, [frameFilter]);

635-636: Replace @ts-expect-error with proper prop typing.

The @ts-expect-error directive suppresses a legitimate TypeScript error. Instead of suppressing it, properly type the styled component props.

Apply this diff to fix the typing:

-// @ts-expect-error TS(7008): Member 'hideRegressions' implicitly has an 'any' t... Remove this comment to see the full error message
-const ProfileVisualizationContainer = styled('div')<{hideRegressions}>`
+const ProfileVisualizationContainer = styled('div')<{hideRegressions: boolean}>`
   display: grid;
   /* false positive for grid layout */
   /* stylelint-disable */
   grid-template-areas: ${p =>
     p.hideRegressions ? "'visualization'" : "'visualization digest'"};
   grid-template-columns: ${p => (p.hideRegressions ? `100%` : `60% 40%`)};
   flex: 1 1 100%;
 `;
src/sentry/snuba/ourlogs.py (1)

159-171: Parameter propagation looks correct.

The equations parameter is properly forwarded to the downstream function using keyword argument syntax, consistent with the other parameters.

Optional observation: The query function (line 37) also has an equations parameter that doesn't appear to be used within the function body. You may want to verify whether this is intentional or if there's additional wiring needed.

src/sentry/replays/usecases/delete.py (1)

87-91: Good None-guard; minor comment nit

Short-circuiting when row["max_segment_id"] is None is a sensible way to avoid range(None + 1) failures and unnecessary storage deletes for replays with no segments. This matches the semantics implied by the Snuba max(segment_id) aggregation.

Nit: consider fixing the comment typo and making it a bit clearer that it is max_segment_id that can be null, e.g.:

-    # Null segment_ids can cause this to fail. If no segments were ingested then we can skip
-    # deleting the segements.
+    # A null max_segment_id indicates no segments were ingested, so we can skip deleting segments.
tests/sentry/replays/tasks/test_delete_replays_bulk.py (1)

95-99: Test data now reflects None max_segment_id; consider adding a direct usecase test

Switching max_segment_id to None here is consistent with the updated delete logic and MatchedRow type, and it ensures the completion path can tolerate rows with no segments.

Note that in this test delete_matched_rows is patched, so the new _make_recording_filenames None-guard isn’t exercised end-to-end. Consider adding a small unit test in src/sentry/replays/usecases/delete.py that asserts _make_recording_filenames(..., {"max_segment_id": None, ...}) returns an empty list and does not attempt any deletes, to prevent regressions in the future.

src/sentry/projectoptions/defaults.py (1)

193-209: Default changed to "off"; consider reusing the shared constant.

The new default "off" is consistent with DEFAULT_AUTOFIX_AUTOMATION_TUNING_DEFAULT and the tests. To avoid future drift, you could import and use that constant here instead of a raw string, e.g.:

-from sentry.constants import TARGET_SAMPLE_RATE_DEFAULT
+from sentry.constants import TARGET_SAMPLE_RATE_DEFAULT, DEFAULT_AUTOFIX_AUTOMATION_TUNING_DEFAULT
@@
-# Should autofix run automatically on new issues
-register(key="sentry:autofix_automation_tuning", default="off")
+# Should autofix run automatically on new issues
+register(
+    key="sentry:autofix_automation_tuning",
+    default=DEFAULT_AUTOFIX_AUTOMATION_TUNING_DEFAULT,
+)
src/sentry/hybridcloud/tasks/deliver_webhooks.py (3)

79-86: Explicit 30s deadline for schedule_webhook_delivery looks reasonable; just ensure it matches expected scheduling cost

The added processing_deadline_duration=30 caps this scheduler task’s runtime, which is good for fairness and avoiding worker starvation, especially with BATCH_SIZE up to 1000 mailboxes. As long as typical runs complete comfortably under 30s at your scale, this is fine; if you see repeated deadline hits, consider either dialing back BATCH_SIZE or nudging the deadline up slightly.


155-162: Align drain_mailbox deadline with BATCH_SCHEDULE_OFFSET expectations

drain_mailbox already has its own in-function deadline via:

  • Line 188: deadline = timezone.now() + BATCH_SCHEDULE_OFFSET
  • Line 192: early exit once timezone.now() >= deadline.

With processing_deadline_duration=300, the effective max runtime becomes min(BATCH_SCHEDULE_OFFSET, 300s). That’s probably desired, but if BACKOFF_INTERVAL is ever configured above 5 minutes, the Taskworker deadline will win and the in-code “delivery_deadline” path might not fire. If that’s not intended, consider either:

  • Documenting that BACKOFF_INTERVAL should not exceed 5 minutes, or
  • Basing processing_deadline_duration directly on BATCH_SCHEDULE_OFFSET.total_seconds() so they remain coupled.

233-240: 120s deadline for drain_mailbox_parallel is consistent but worth sanity‑checking against batch size and worker_threads

The new processing_deadline_duration=120 caps the parallel drain, which is good for preventing long‑running mailboxes from monopolizing a worker. Since the inner loop processes at most worker_threads records per iteration and respects its own deadline = timezone.now() + BATCH_SCHEDULE_OFFSET (line 294), the same “min(external, internal)” runtime behavior applies here.

It’s probably safe, but keep an eye on telemetry: if parallel drains regularly approach or exceed 120s (especially with higher worker_threads), you may want to either:

  • Slightly increase the deadline, or
  • Trim worker_threads / batch sizes so that each task comfortably completes within 120s.
src/sentry/tasks/auth/check_auth.py (1)

72-79: 60s deadline is fine, but there’s a subtle interaction with last_synced updates that’s worth revisiting

Adding processing_deadline_duration=60 is a good safeguard, but note the flow:

  • check_auth (line 44–58) updates last_synced for the whole identity_ids_list before enqueuing check_auth_identities.
  • check_auth_identities (lines 80–99) then loops over auth_identity_ids and calls check_single_auth_identity per ID, with no internal time cutoff.

If the task hits the 60s processing deadline mid‑loop, some identities in the chunk will have last_synced bumped but will never actually be refreshed until they age back past the cutoff again. That was already a risk on crashes, but introducing a hard time limit makes it more systematic under load.

Consider one of:

  • Moving the last_synced update into check_auth_identities as identities are processed, or
  • Reducing chunk_size / dynamically checking elapsed time inside check_auth_identities and re‑queueing any remaining IDs in a follow‑up task, so no identity is permanently “marked processed” without being refreshed.

Also, for consistency, you may want to decide whether the deprecated check_auth_identity task should get a similar deadline or be retired once this path is fully rolled out.

static/app/utils/analytics/replayAnalyticsEvents.tsx (1)

116-117: Consider adding specific parameters to zoom events.

While the current Record<string, unknown> type works, adding explicit parameters like scale or zoom_level would make the analytics data more actionable and align with the specificity of other events in this file (e.g., replay.play-pause includes context, mobile, play, etc.).

Apply this diff to add more specific typing:

-  'replay.timeline.zoom-in': Record<string, unknown>;
-  'replay.timeline.zoom-out': Record<string, unknown>;
+  'replay.timeline.zoom-in': {
+    new_scale: number;
+  };
+  'replay.timeline.zoom-out': {
+    new_scale: number;
+  };

Then update the tracking calls in static/app/components/replays/timeAndScrubberGrid.tsx to include the scale value:

trackAnalytics('replay.timeline.zoom-out', {
  organization,
  new_scale: newScale,
});

trackAnalytics('replay.timeline.zoom-in', {
  organization,
  new_scale: newScale,
});

Also applies to: 158-159

static/app/views/alerts/list/rules/row.tsx (1)

269-278: Remove unnecessary nested Flex wrapper.

The inner Flex at lines 270-272 wraps only CombinedAlertBadge and adds no layout value since the outer Flex already provides align="center".

Apply this diff to simplify the structure:

 <Flex align="center">
-  <Flex align="center">
-    <CombinedAlertBadge rule={rule} />
-  </Flex>
+  <CombinedAlertBadge rule={rule} />
   {!isUptime && !isCron && (
     <MarginLeft>
       <AlertRuleStatus rule={rule} />
     </MarginLeft>
   )}
 </Flex>
src/sentry/grouping/parameterization.py (1)

335-350: Remove now-unused _handle_regex_match in parametrize_w_experiments

With all experiments funneled through experiment.run(content, _incr_counter), the _handle_regex_match helper inside parametrize_w_experiments is dead code and duplicates the logic already used in parametrize_w_regex. Dropping it will reduce confusion without changing behavior.

-        def _handle_regex_match(match: re.Match[str]) -> str:
-            # Find the first (should be only) non-None match entry, and sub in the placeholder. For
-            # example, given the groupdict item `('hex', '0x40000015')`, this returns '<hex>' as a
-            # replacement for the original value in the string.
-            for key, value in match.groupdict().items():
-                if value is not None:
-                    self.matches_counter[key] += 1
-                    return f"<{key}>"
-            return ""
-
-        for experiment in self._experiments:
+        for experiment in self._experiments:
             if not should_run(experiment.name):
                 continue
 
             content = experiment.run(content, _incr_counter)
src/sentry/preprod/__init__.py (1)

1-1: Consider explicit exports for better maintainability.

Wildcard imports make it unclear which symbols are being exported and can lead to namespace pollution. Consider using an explicit __all__ list to document the public API.

Apply this diff to make exports explicit:

-from .analytics import *  # NOQA
+from .analytics import PreprodArtifactApiAssembleEvent
+
+__all__ = ["PreprodArtifactApiAssembleEvent"]
src/sentry/issues/endpoints/browser_reporting_collector.py (1)

126-126: Remove redundant str() call.

The type field is already validated as a ChoiceField with string choices, so browser_report["type"] is guaranteed to be a string. The explicit str() conversion is unnecessary.

Apply this diff:

             metrics.incr(
                 "browser_reporting.raw_report_received",
-                tags={"browser_report_type": str(browser_report["type"])},
+                tags={"browser_report_type": browser_report["type"]},
                 sample_rate=1.0,  # XXX: Remove this once we have a ballpark figure
             )
src/sentry/feedback/usecases/feedback_summaries.py (1)

51-54: Consider adding validation for prompt constraints.

The parse_response function doesn't verify that the LLM output adheres to the constraints specified in the prompt (55-word maximum, 2-sentence maximum, "Users..." prefix). While LLMs typically follow instructions, adding validation could help catch edge cases and provide better error handling.

Example validation to consider:

def parse_response(text):
    cleaned = re.sub(r"\s+", " ", text).strip()
    
    # Validate word count
    word_count = len(cleaned.split())
    if word_count > 55:
        logger.warning(f"Summary exceeds 55 words: {word_count} words")
    
    # Validate sentence count
    sentence_count = len(re.split(r'[.!?]+', cleaned.strip()))
    if sentence_count > 2:
        logger.warning(f"Summary exceeds 2 sentences: {sentence_count} sentences")
    
    return cleaned
static/app/views/feedback/feedbackListPage.tsx (1)

125-129: Consider a slightly larger gap for better visual separation.

The current gap of space(1) (typically 8px) between the summary and list might be too small given that the summary is a visually distinct AI-generated component. Consider using space(1.5) or space(2) for clearer separation.

 const SummaryListContainer = styled('div')`
   display: flex;
   flex-direction: column;
-  gap: ${space(1)};
+  gap: ${space(2)};
 `;
static/app/components/feedback/feedbackSummary.tsx (1)

15-21: Redundant feature flag check.

The feature flag is checked both in the hook's enabled condition (line 36-38 of useFeedbackSummary.tsx) and here at line 16. While this defensive approach is safe, the check here is redundant since the hook won't fetch data when the flag is disabled.

Consider simplifying to:

  if (
-   !organization.features.includes('user-feedback-ai-summaries') ||
    tooFewFeedbacks ||
    isError
  ) {
    return null;
  }
static/app/views/explore/spans/spansTab.tsx (1)

346-356: LGTM with optional refactor suggestion.

The change correctly adapts to the single-axis model. The code wraps the single yAxis in an array and immediately flatMaps over it, which works correctly.

Consider simplifying the series construction to avoid the wrap/unwrap pattern:

-    const dedupedYAxes = [visualize.yAxis];
-    const series = dedupedYAxes
-      .flatMap(yAxis => timeseriesResult.data[yAxis])
-      .filter(defined);
+    const series = (timeseriesResult.data[visualize.yAxis] ?? []).filter(defined);

This would eliminate the intermediate array and flatMap while achieving the same result.

src/sentry/integrations/source_code_management/commit_context.py (1)

142-144: New PR comment helpers look solid; consider clarifying title-length semantics

The _truncate_title / template helpers and environment extraction are implemented safely and readably. Two small nits you may want to consider:

  • ISSUE_TITLE_MAX_LENGTH currently caps the pre‑ellipsis length; the final rendered title can exceed that length by up to 3 chars once "..." is appended. If you need a hard cap on the total length (e.g. provider limits), you might want to subtract the ellipsis length from the slice or rename the constant to reflect “max base length.”
  • get_environment_info logs at info on any exception; if this ends up noisy in practice, downgrading to debug (or adding a dedicated sampling key) might be preferable, since failure to fetch environment is non-critical.

Otherwise this is a nice, reusable abstraction for PR comment formatting.

Also applies to: 577-607

static/app/views/explore/hooks/useAddToDashboard.tsx (1)

50-58: Dashboard add flow is correctly migrated to single yAxis; invariant on visualizes still implicit

Using const yAxis = visualizes[visualizeIndex]!.yAxis and passing yAxis: [yAxis] into the discover query cleanly adapts this hook to the single‑axis model while preserving the existing fields/sorts behavior.

This function continues to assume that visualizes[visualizeIndex] always exists (the non‑null assertion, plus chartType access below). If there’s any path where visualizes could be empty or the index out of range, it may be worth adding a small guard/early return to avoid runtime errors, but if the invariant is guaranteed by the caller, the current code is fine.

Also applies to: 64-71

static/app/views/explore/toolbar/toolbarSaveAs.tsx (1)

61-62: Single-yAxis migration looks good; consider deduping alert options and making compare invariant explicit

The switch to a single yAxis per visualize is wired through consistently here:

  • visualizeYAxes = visualizes.map(v => v.yAxis) feeds both alert URLs and the compare route.
  • Chart options now treat each chart as having exactly one axis ([chart.yAxis]), which matches the new Visualize shape.
  • The compare route payload uses yAxes: [visualizeYAxes[0]!], preserving the existing multi-query API contract while adapting to a single local axis.

Two small follow-ups you might want to consider:

  1. Alert submenu duplication: alertsUrls now maps directly over visualizeYAxes without deduplication. If multiple charts share the same aggregate, users will see repeated “An Alert for <metric>” entries where previously dedupeArray avoided this. A lightweight fix would be:
const uniqueYAxes = Array.from(new Set(visualizeYAxes));
const alertsUrls = uniqueYAxes.map((yAxis, index) => { /* ... */ });
  1. Compare route assumptions: Both visualizeYAxes[0]! and visualizes[0]!.chartType assume there is at least one visualize. If that invariant isn’t strictly guaranteed upstream, a simple guard (e.g. early return or disabling the compare button) would prevent a runtime crash.

Functionally, though, the single‑axis migration is sound.

Also applies to: 72-95, 152-175, 296-298

static/app/views/explore/utils.tsx (1)

283-295: Explore utils are correctly adapted to single yAxis; minor cleanup possible

The changes in both helpers align with the new Visualize shape:

  • viewSamplesTarget now derives the underlying field from visualize.yAxis via parseFunction, preserving the previous “first argument becomes column” behavior while avoiding any yAxes iteration.
  • computeVisualizeSampleTotals now effectively aggregates series for a single axis per visualize, which matches how charts are defined elsewhere after the refactor.

Given each Visualize now has only one axis, dedupedYAxes = [visualize.yAxis]; is a bit of leftover structure. You could simplify to:

const series = (data[visualize.yAxis] ?? []).filter(defined);

(or whatever the concrete type for data[visualize.yAxis] is), which would be slightly clearer without changing behavior.

Functionally, the current code is sound.

Also applies to: 433-443

static/app/views/codecov/tests/onboardingSteps/addUploadToken.tsx (1)

6-6: Token rows Flex usage looks fine; minor duplication opportunity

The nested <Flex justify="space-between" gap={space(1)}> blocks correctly lay out the key/token pair against the action button in both full and truncated token states, matching the intended “left text, right button” behavior.

If you want to reduce repetition, you could extract a tiny TokenRow helper that takes {token, buttonLabel, buttonPriority, onClick} and renders this Flex structure once, but that’s purely optional given the small size here.

Also applies to: 77-97

tests/js/fixtures/tabularColumns.ts (1)

1-9: TabularColumnsFixture helper looks good

Straightforward wrapper around TabularColumnFixture; types are tight and behavior is clear. If you want to trim noise, you can omit the explicit param: Partial<TabularColumn> in the map callback and rely on inference, but that's purely optional.

static/app/views/dashboards/widgets/tableWidget/tableWidgetVisualization.spec.tsx (1)

14-106: Test coverage for TableWidgetVisualization looks solid

The spec exercises the key paths: default rendering, custom column order/display names, formatter behavior for duration/percentage/rate, and custom head/body renderers. This gives good confidence in the new table widget behavior.

If you find these tests getting brittle over time, you might consider asserting on column labels via more semantic queries (e.g. getByRole('columnheader', {name: /.../})), but what you have here is fine for now.

static/app/views/dashboards/widgets/tableWidget/defaultTableCellRenderers.tsx (1)

24-42: Align on field key for alignment and tighten types in default renderers

The default renderers look good overall; a few small tweaks would make them more robust and consistent:

  1. Use the field key for alignment, not the display name

Right now alignment is derived from column.name:

const align = fieldAlignment(column.name, column.type as ColumnValueType);

If callers supply a custom name (e.g. "Count of Span Duration"), this may diverge from the actual field key (e.g. "count(span.duration)"). Using the key is safer:

- const align = fieldAlignment(column.name, column.type as ColumnValueType);
+ const align = fieldAlignment(String(column.key), column.type as ColumnValueType);
  1. Avoid over‑narrowing the unit to string

unit is currently cast to string:

const unit = tableData.meta.units?.[columnKey] as string;

That drops potential type information (e.g. a DurationUnit enum) and can misrepresent undefined. Prefer preserving the declared unit type:

- const unit = tableData.meta.units?.[columnKey] as string;
+ const unit = tableData.meta.units?.[columnKey];
  1. Optional: reuse CellWrapper for body cells for consistent layout

If you want body cells to share the same nowrap/full‑width behavior as headers, you could wrap the rendered value similarly, using the same alignment logic on the field key. That’s not strictly necessary, but can improve visual consistency.

All of these are incremental and safe to defer, but they’ll make the defaults a bit more future‑proof.

Also applies to: 75-83, 95-104

static/app/views/dashboards/widgets/tableWidget/tableWidgetVisualization.tsx (1)

91-99: Guard column derivation against missing meta

columnOrder falls back to deriving columns from tableData.meta.fields:

const columnOrder: TabularColumn[] =
  columns ??
  Object.keys(tableData?.meta.fields).map((key: string) => ({
    key,
    name: key,
    width: -1,
    type: tableData?.meta.fields[key],
  }));

Assuming TabularData.meta.fields is always present this is fine, but if a caller ever passes a partially populated tableData (e.g. in a loading or error state), tableData.meta being undefined would cause Object.keys to throw.

You can make this a bit more defensive at low cost:

-  Object.keys(tableData?.meta.fields).map((key: string) => ({
+  Object.keys(tableData?.meta?.fields ?? {}).map((key: string) => ({
     key,
     name: key,
     width: -1,
-    type: tableData?.meta.fields[key],
+    type: tableData?.meta?.fields?.[key],
   }));

This keeps the existing behavior when meta is present, but degrades gracefully to an empty column set otherwise.

static/app/views/dashboards/widgets/tableWidget/tableWidgetVisualization.stories.tsx (3)

13-33: Solid introductory story; minor copy nits

The basic example is clear and correctly wires TableWidgetVisualization with sampleHTTPRequestTableData. Consider tightening the copy as a low-priority polish:

  • Line 27: "Below is the the most basic example..." → remove duplicated "the".
  • Line 29: Add a period at the end of the sentence.

These are purely editorial and optional.


35-89: Good coverage of data/columns behavior; consider prettifying JSON output

The story clearly illustrates:

  • Empty data handling via tableWithEmptyData.
  • Optional columns override vs meta.fields fallback.
  • Custom column naming and ordering.

One optional improvement for readability in the snippet:

-${JSON.stringify(tableWithEmptyData)}
+${JSON.stringify(tableWithEmptyData, null, 2)}

This will render nicely formatted, multi-line JSON in <CodeSnippet language="json">, making the example easier to scan.


91-128: Custom cell rendering example is clear and aligned with intended API

The story effectively demonstrates:

  • Header customization via renderTableHeadCell.
  • Body customization via renderTableBodyCell.
  • The pattern of returning undefined to fall back to default rendering for selected columns.

As a tiny polish, you could add a period at the end of the sentence on lines 113–115 (“allowing for partial custom rendering.”), but that’s optional.

static/app/views/explore/types.tsx (1)

1-21: Clarify base-props docs vs actual usage

The shared UseTraceItemAttributeBaseProps shape looks good, but the doc comments (“currently only supports LOGS” / “string and number”) may already be outdated given SPANS and new hooks using this type. Consider updating the comments (or tightening the type) so consumers aren’t misled about supported datasets and attribute types.

static/app/views/explore/hooks/useGetTraceItemAttributeValues.spec.tsx (1)

13-16: Tests match new hook behavior; optional cleanup

The tests correctly validate that string attributes hit the /values/ endpoint and number attributes always return an empty array without issuing a request. In the “number type” test, the mock response is never used; you could drop that mock entirely to make the intent (no network call) even clearer.

Also applies to: 30-33, 57-101, 103-148

static/app/views/explore/hooks/useGetTraceItemAttributeValues.tsx (1)

12-28: Align UseGetTraceItemAttributeValuesProps with base props usage

The hook behavior (early-return for functions/numbers, query construction, and filtering out undefined values) looks solid. However, UseGetTraceItemAttributeValuesProps extends UseTraceItemAttributeBaseProps but only uses projectIds, ignoring the projects?: Project[] field that the base type advertises. That differs from useTraceItemAttributeKeys, which does respect projects.

To avoid surprising consumers who might pass projects here, consider either:

  • Wiring projects through, similar to the keys hook:
-export function useGetTraceItemAttributeValues({
-  traceItemType,
-  projectIds,
-  datetime,
-  type = 'string',
-}: UseGetTraceItemAttributeValuesProps) {
+export function useGetTraceItemAttributeValues({
+  traceItemType,
+  projectIds,
+  datetime,
+  type = 'string',
+  projects,
+}: UseGetTraceItemAttributeValuesProps) {
  ...
-      const queryKey = traceItemAttributeValuesQueryKey({
+      const queryKey = traceItemAttributeValuesQueryKey({
         orgSlug: organization.slug,
         attributeKey: tag.key,
         search: queryString,
-        projectIds: projectIds ?? selection.projects,
+        projectIds:
+          projectIds ??
+          (projects ? projects.map(project => project.id) : selection.projects),
         datetime: datetime ?? selection.datetime,
         traceItemType,
         type,
       });
@@
-    [
-      api,
-      type,
-      organization.slug,
-      projectIds,
-      selection.projects,
-      selection.datetime,
-      datetime,
-      traceItemType,
-    ]
+    [
+      api,
+      type,
+      organization.slug,
+      projectIds,
+      projects,
+      selection.projects,
+      selection.datetime,
+      datetime,
+      traceItemType,
+    ]
  • Or, if you don’t intend this hook to accept projects, stop extending the base props and define a leaner props type.

Either approach will make the API surface less ambiguous.

Also applies to: 78-104, 118-127

tests/sentry/replays/test_project_replay_summarize_breadcrumbs.py (1)

283-331: test_get_request_data correctly validates interleaving; minor robustness opportunity

This test does a good job of pinning the desired ordering between two breadcrumb events and two ErrorEvents at different timestamps, ensuring get_request_data() yields [error-before, log1, log2, error-after]. That gives solid protection around the chronological merge logic.

If you later change get_request_data() to avoid in-place sorting (e.g., switching to sorted() as suggested in the endpoint file), this test will continue to hold without modification, which is ideal.

If you want to go a step further, you could also assert that the original error_events list ordering is preserved after the call (i.e., the helper doesn’t mutate its inputs), but that’s optional and only needed if callers start reusing the list elsewhere.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49a2758 and ea188e2.

📒 Files selected for processing (106)
  • devservices/config.yml (4 hunks)
  • src/sentry/constants.py (1 hunks)
  • src/sentry/feedback/usecases/feedback_summaries.py (1 hunks)
  • src/sentry/grouping/parameterization.py (2 hunks)
  • src/sentry/hybridcloud/tasks/deliver_webhooks.py (3 hunks)
  • src/sentry/integrations/github/integration.py (1 hunks)
  • src/sentry/integrations/gitlab/integration.py (1 hunks)
  • src/sentry/integrations/source_code_management/commit_context.py (2 hunks)
  • src/sentry/issues/endpoints/browser_reporting_collector.py (4 hunks)
  • src/sentry/issues/grouptype.py (1 hunks)
  • src/sentry/migrations/0917_convert_org_saved_searches_to_views.py (1 hunks)
  • src/sentry/migrations/0920_convert_org_saved_searches_to_views_revised.py (1 hunks)
  • src/sentry/options/defaults.py (0 hunks)
  • src/sentry/preprod/__init__.py (1 hunks)
  • src/sentry/preprod/analytics.py (1 hunks)
  • src/sentry/preprod/api/endpoints/organization_preprod_artifact_assemble.py (2 hunks)
  • src/sentry/projectoptions/defaults.py (1 hunks)
  • src/sentry/replays/endpoints/project_replay_summarize_breadcrumbs.py (5 hunks)
  • src/sentry/replays/usecases/delete.py (2 hunks)
  • src/sentry/snuba/ourlogs.py (2 hunks)
  • src/sentry/tasks/auth/check_auth.py (1 hunks)
  • src/sentry/workflow_engine/endpoints/validators/base/detector.py (4 hunks)
  • src/sentry/workflow_engine/processors/delayed_workflow.py (6 hunks)
  • src/sentry/workflow_engine/processors/workflow.py (1 hunks)
  • static/app/components/codeSnippet.tsx (2 hunks)
  • static/app/components/codecov/branchSelector/branchSelector.tsx (2 hunks)
  • static/app/components/codecov/datePicker/dateSelector.tsx (2 hunks)
  • static/app/components/codecov/integratedOrgSelector/integratedOrgSelector.tsx (4 hunks)
  • static/app/components/codecov/repoPicker/repoSelector.tsx (2 hunks)
  • static/app/components/core/button/styles.chonk.tsx (1 hunks)
  • static/app/components/events/eventAttachments.tsx (2 hunks)
  • static/app/components/events/groupingInfo/groupingVariant.tsx (1 hunks)
  • static/app/components/events/interfaces/spans/newTraceDetailsHeader.tsx (4 hunks)
  • static/app/components/feedback/feedbackSummary.tsx (1 hunks)
  • static/app/components/feedback/list/useFeedbackSummary.tsx (1 hunks)
  • static/app/components/group/times.tsx (3 hunks)
  • static/app/components/replays/breadcrumbs/breadcrumbItem.tsx (1 hunks)
  • static/app/components/replays/timeAndScrubberGrid.tsx (4 hunks)
  • static/app/components/scrollCarousel.tsx (1 hunks)
  • static/app/utils/analytics/replayAnalyticsEvents.tsx (2 hunks)
  • static/app/views/alerts/list/rules/alertRuleStatus.tsx (3 hunks)
  • static/app/views/alerts/list/rules/row.tsx (3 hunks)
  • static/app/views/codecov/tests/onboardingSteps/addUploadToken.tsx (2 hunks)
  • static/app/views/dashboards/widgetCard/chart.tsx (4 hunks)
  • static/app/views/dashboards/widgets/common/types.tsx (1 hunks)
  • static/app/views/dashboards/widgets/tableWidget/defaultTableCellRenderers.tsx (1 hunks)
  • static/app/views/dashboards/widgets/tableWidget/fixtures/sampleHTTPRequestTableData.ts (1 hunks)
  • static/app/views/dashboards/widgets/tableWidget/tableWidgetVisualization.spec.tsx (1 hunks)
  • static/app/views/dashboards/widgets/tableWidget/tableWidgetVisualization.stories.tsx (1 hunks)
  • static/app/views/dashboards/widgets/tableWidget/tableWidgetVisualization.tsx (1 hunks)
  • static/app/views/explore/charts/index.tsx (4 hunks)
  • static/app/views/explore/components/traceItemSearchQueryBuilder.tsx (2 hunks)
  • static/app/views/explore/contexts/pageParamsContext/aggregateFields.tsx (4 hunks)
  • static/app/views/explore/contexts/pageParamsContext/index.spec.tsx (17 hunks)
  • static/app/views/explore/contexts/pageParamsContext/index.tsx (4 hunks)
  • static/app/views/explore/contexts/pageParamsContext/sortBys.tsx (2 hunks)
  • static/app/views/explore/contexts/pageParamsContext/visualizes.spec.tsx (3 hunks)
  • static/app/views/explore/contexts/pageParamsContext/visualizes.tsx (4 hunks)
  • static/app/views/explore/hooks/useAddToDashboard.tsx (2 hunks)
  • static/app/views/explore/hooks/useAnalytics.tsx (5 hunks)
  • static/app/views/explore/hooks/useExploreAggregatesTable.tsx (1 hunks)
  • static/app/views/explore/hooks/useExploreTimeseries.tsx (3 hunks)
  • static/app/views/explore/hooks/useGetTraceItemAttributeKeys.tsx (1 hunks)
  • static/app/views/explore/hooks/useGetTraceItemAttributeValues.spec.tsx (4 hunks)
  • static/app/views/explore/hooks/useGetTraceItemAttributeValues.tsx (3 hunks)
  • static/app/views/explore/hooks/useTopEvents.tsx (1 hunks)
  • static/app/views/explore/hooks/useTraceItemAttributeKeys.tsx (2 hunks)
  • static/app/views/explore/spans/spansTab.tsx (1 hunks)
  • static/app/views/explore/tables/aggregateColumnEditorModal.spec.tsx (14 hunks)
  • static/app/views/explore/tables/aggregateColumnEditorModal.tsx (4 hunks)
  • static/app/views/explore/toolbar/index.spec.tsx (11 hunks)
  • static/app/views/explore/toolbar/toolbarSaveAs.tsx (2 hunks)
  • static/app/views/explore/toolbar/toolbarSortBy.tsx (1 hunks)
  • static/app/views/explore/toolbar/toolbarVisualize.tsx (6 hunks)
  • static/app/views/explore/types.tsx (1 hunks)
  • static/app/views/explore/utils.spec.tsx (1 hunks)
  • static/app/views/explore/utils.tsx (2 hunks)
  • static/app/views/feedback/feedbackListPage.tsx (4 hunks)
  • static/app/views/insights/common/components/chartActionDropdown.tsx (1 hunks)
  • static/app/views/insights/pages/transactionNameSearchBar.tsx (2 hunks)
  • static/app/views/organizationStats/teamInsights/teamMisery.tsx (3 hunks)
  • static/app/views/performance/newTraceDetails/traceDrawer/details/span/eapSections/attributes.tsx (2 hunks)
  • static/app/views/performance/newTraceDetails/traceTabsAndVitals.tsx (4 hunks)
  • static/app/views/performance/newTraceDetails/traceWaterfall.tsx (3 hunks)
  • static/app/views/profiling/profileSummary/index.tsx (1 hunks)
  • static/app/views/replays/detail/ai/index.tsx (2 hunks)
  • static/app/views/settings/dynamicSampling/organizationSampleRateInput.tsx (4 hunks)
  • static/app/views/settings/organizationAuditLog/auditLogList.tsx (2 hunks)
  • static/app/views/settings/organizationIntegrations/detailedView/integrationLayout.tsx (2 hunks)
  • static/app/views/settings/project/projectOwnership/codeOwnerFileTable.tsx (4 hunks)
  • static/gsAdmin/views/instanceLevelOAuth/instanceLevelOAuthDetails.tsx (3 hunks)
  • tests/js/fixtures/tabularColumn.ts (1 hunks)
  • tests/js/fixtures/tabularColumns.ts (1 hunks)
  • tests/sentry/api/endpoints/test_browser_reporting_collector.py (4 hunks)
  • tests/sentry/api/endpoints/test_project_details.py (1 hunks)
  • tests/sentry/api/serializers/test_project.py (1 hunks)
  • tests/sentry/grouping/test_parameterization.py (1 hunks)
  • tests/sentry/integrations/github/tasks/test_pr_comment.py (6 hunks)
  • tests/sentry/integrations/gitlab/tasks/test_pr_comment.py (6 hunks)
  • tests/sentry/migrations/test_0917_convert_org_saved_searches_to_views.py (0 hunks)
  • tests/sentry/replays/tasks/test_delete_replays_bulk.py (1 hunks)
  • tests/sentry/replays/test_project_replay_summarize_breadcrumbs.py (5 hunks)
  • tests/sentry/workflow_engine/endpoints/test_organization_detector_details.py (1 hunks)
  • tests/sentry/workflow_engine/endpoints/test_organization_detector_index.py (1 hunks)
  • tests/sentry/workflow_engine/processors/test_delayed_workflow.py (6 hunks)
  • tests/snuba/api/endpoints/test_organization_events_stats.py (4 hunks)
💤 Files with no reviewable changes (2)
  • src/sentry/options/defaults.py
  • tests/sentry/migrations/test_0917_convert_org_saved_searches_to_views.py

Comment on lines +36 to +60
class BrowserReportSerializer(serializers.Serializer[Any]):
"""Serializer for validating browser report data structure."""

body = serializers.DictField()
type = serializers.ChoiceField(choices=BROWSER_REPORT_TYPES)
url = serializers.URLField()
user_agent = serializers.CharField()
destination = serializers.CharField()
attempts = serializers.IntegerField(min_value=1)
# Fields that do not overlap between specs
# We need to support both specs
age = serializers.IntegerField(required=False)
timestamp = serializers.IntegerField(required=False, min_value=0)

def validate_timestamp(self, value: int) -> int:
"""Validate that age is absent, but timestamp is present."""
if self.initial_data.get("age"):
raise serializers.ValidationError("If timestamp is present, age must be absent")
return value

def validate_age(self, value: int) -> int:
"""Validate that age is present, but not timestamp."""
if self.initial_data.get("timestamp"):
raise serializers.ValidationError("If age is present, timestamp must be absent")
return value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Consider validating that at least one of age or timestamp is present.

The W3C specs referenced in the comments indicate two different standards: Working Draft uses timestamp while Editor's Draft uses age. The current validators ensure mutual exclusion (both fields cannot be present simultaneously), but neither validator ensures at least one is present.

If both fields are optional and absent, no validation error will be raised, which may lead to incomplete report data.

Apply this diff to add a class-level validator:

     def validate_age(self, value: int) -> int:
         """Validate that age is present, but not timestamp."""
         if self.initial_data.get("timestamp"):
             raise serializers.ValidationError("If age is present, timestamp must be absent")
         return value
+
+    def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
+        """Ensure at least one of age or timestamp is present."""
+        if "age" not in attrs and "timestamp" not in attrs:
+            raise serializers.ValidationError(
+                "Either 'age' or 'timestamp' must be present per W3C Reporting API specs"
+            )
+        return attrs
🤖 Prompt for AI Agents
In src/sentry/issues/endpoints/browser_reporting_collector.py around lines 36 to
60, add a class-level validation to ensure at least one of age or timestamp is
provided: implement a validate(self, attrs) method that checks if neither
attrs.get("age") nor attrs.get("timestamp") is present and raises a
serializers.ValidationError indicating that one of age or timestamp must be
supplied (attach error to a non-field error or to both fields for clarity); keep
the existing per-field mutual-exclusion validators intact.

Comment on lines +81 to +86
analytics.record(
"preprod_artifact.api.assemble",
organization_id=project.organization_id,
project_id=project.id,
user_id=request.user.id,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle potential AttributeError when accessing user ID.

Line 85 accesses request.user.id without checking if the user is authenticated. For anonymous users or certain authentication types, request.user might not have an id attribute, causing an AttributeError.

Apply this diff to safely access the user ID:

 analytics.record(
     "preprod_artifact.api.assemble",
     organization_id=project.organization_id,
     project_id=project.id,
-    user_id=request.user.id,
+    user_id=getattr(request.user, 'id', None),
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
analytics.record(
"preprod_artifact.api.assemble",
organization_id=project.organization_id,
project_id=project.id,
user_id=request.user.id,
)
analytics.record(
"preprod_artifact.api.assemble",
organization_id=project.organization_id,
project_id=project.id,
user_id=getattr(request.user, 'id', None),
)
🤖 Prompt for AI Agents
In src/sentry/preprod/api/endpoints/organization_preprod_artifact_assemble.py
around lines 81 to 86, the analytics call uses request.user.id without guarding
against anonymous or non-standard user objects; replace that direct access with
a safe retrieval (e.g., use getattr(request.user, "id", None) or check
hasattr(request.user, "id") and pass None when absent) so the analytics.record
call never raises AttributeError for unauthenticated users.

background: ${p =>
p.transparentMask
? 'linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1))'
? `linear-gradient(to right, transparent, ${p.theme.background})`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Inconsistency with LeftMask causes incorrect gradient interpolation.

The RightMask uses the CSS transparent keyword, which is equivalent to rgba(0, 0, 0, 0) (transparent black). This creates incorrect color interpolation, especially noticeable in light themes where the gradient will transition through grey tones instead of smoothly fading from the theme background to transparent.

The LeftMask correctly uses Color(p.theme.background).alpha(0).rgb().string() to create a transparent version of the theme background color. Both masks should use the same approach for visual consistency and correct color interpolation.

Apply this diff to fix the inconsistency:

-      ? `linear-gradient(to right, transparent, ${p.theme.background})`
+      ? `linear-gradient(to right, ${Color(p.theme.background).alpha(0).rgb().string()}, ${p.theme.background})`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
? `linear-gradient(to right, transparent, ${p.theme.background})`
? `linear-gradient(to right, ${Color(p.theme.background).alpha(0).rgb().string()}, ${p.theme.background})`
🤖 Prompt for AI Agents
In static/app/components/scrollCarousel.tsx around line 204, the RightMask
currently uses the CSS keyword "transparent" which interpolates from transparent
black and causes wrong gradient colors in light themes; change the right-side
gradient stop to use the same transparentized theme background as LeftMask by
replacing "transparent" with Color(p.theme.background).alpha(0).rgb().string()
(i.e., build a transparent version of the theme background color) so both masks
use identical color interpolation and visual behavior.

assert set(data.keys()) == {x[0] for x in expected_message_counts[:5]}.union({"Other"})

for index, (message, count) in enumerate(expected_message_counts[:5]):
assert [{"count": count}] in data[message]["data"][0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect assertion pattern.

The assertions directly access data[message]["data"][0] (a single tuple), checking if a list is inside it. This doesn't match the pattern used in the spans tests and will likely fail or give incorrect results.

Apply this diff to fix the assertion pattern:

         for index, (message, count) in enumerate(expected_message_counts[:5]):
-            assert [{"count": count}] in data[message]["data"][0]
+            assert [{"count": count}] in [attrs for _, attrs in data[message]["data"]]
             assert data[message]["order"] == index
 
         other = data["Other"]
         assert other["order"] == 5
-        assert [{"count": 10}] in other["data"][0]
+        assert [{"count": 10}] in [attrs for _, attrs in other["data"]]

Also applies to: 3189-3189

🤖 Prompt for AI Agents
In tests/snuba/api/endpoints/test_organization_events_stats.py around lines 3184
and 3189, the test currently does `assert [{"count": count}] in
data[message]["data"][0]` which is wrong because `data[message]["data"][0]` is a
tuple; update both assertions to check inside the tuple's values instead — e.g.
assert [{"count": count}] in data[message]["data"][0][1] — so you're asserting
the expected dict list is present in the tuple's payload rather than in the
tuple itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.