Skip to content

feat(span-first): Port native app start integration to V2 span API#3534

Merged
buenaflor merged 43 commits intofeat/span-firstfrom
feat/span/native-app-start-v2
Mar 18, 2026
Merged

feat(span-first): Port native app start integration to V2 span API#3534
buenaflor merged 43 commits intofeat/span-firstfrom
feat/span/native-app-start-v2

Conversation

@buenaflor
Copy link
Copy Markdown
Contributor

@buenaflor buenaflor commented Feb 20, 2026

📜 Description

Ports the native app start and generic app start integrations to the V2 streaming span API, gated by SentryTraceLifecycle.streaming.

💡 Motivation and Context

Span-first and closes #3334

💚 How did you test it?

Unit tests, manual tests

📝 Checklist

  • I reviewed submitted code
  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPii is enabled
  • I updated the docs if needed
  • All tests passing
  • No breaking changes

🔮 Next steps

Make sure mobile vitals insights product page works

#skip-changelog

buenaflor and others added 15 commits February 19, 2026 21:17
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace inline TTID/TTFD streaming code in SentryNavigatorObserver
with TimeToDisplayTrackerV2.trackRoute(). Remove static ttfdSpan field
and route SentryDisplay/SentryFlutter.currentDisplay() through the
tracker on SentryFlutterOptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aming integration tests

Make timeToDisplayTrackerV2 a non-nullable late field on SentryFlutterOptions
instead of being created and set by SentryNavigatorObserver. Add didPop
handling for streaming mode and integration tests covering the full span v2
TTID/TTFD lifecycle through the navigator observer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add V2 streaming span support to the native and generic app start
integrations, gated by SentryTraceLifecycle.streaming.

Key changes:
- Add startTimestamp param to V2 span creation chain (Hub, Recording/
  IdleRecordingSentrySpanV2) for backdated app start spans
- Extract shared AppStartInfo/TimeSpan/parseNativeAppStart into
  native_app_start_data.dart for reuse across V1 and V2 handlers
- Extend TimeToDisplayTrackerV2.trackRoute() to return SentrySpanV2
  and accept optional startTimestamp/ttidEndTimestamp params
- Create NativeAppStartHandlerV2 that uses trackRoute() with backdated
  timestamps and creates app start + phase + native child spans
- Branch NativeAppStartIntegration and GenericAppStartIntegration on
  traceLifecycle to delegate to V1 or V2 handlers

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

🚨 Detected changes in high risk code 🚨

High-risk code has higher potential to break the SDK and may be hard to test. To prevent severe bugs, apply the rollout process for releasing such changes and be extra careful when changing and reviewing these files:

  • packages/flutter/lib/src/integrations/native_app_start_integration.dart

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 20, 2026

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against d11c441

@buenaflor buenaflor changed the base branch from main to feat/span-first February 20, 2026 17:56
@buenaflor buenaflor changed the base branch from feat/span-first to feat/span/ttid-ttfd February 20, 2026 17:57
@buenaflor buenaflor changed the title feat(tracing): Port native app start integration to V2 span API feat(span-first): Port native app start integration to V2 span API Feb 20, 2026
buenaflor and others added 3 commits February 23, 2026 11:27
Create root idle span synchronously during integration init via
prepareRouteSpan() so user spans in initState can parent to it.
When native app start data arrives, trackRoute() reuses the prepared
span and backdates timestamps instead of creating a new one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Base automatically changed from feat/span/ttid-ttfd to feat/span-first February 23, 2026 15:08
buenaflor and others added 6 commits February 23, 2026 16:33
Replace outdated `trackRoute()` calls with the current API methods:
`trackRootNavigation`, `trackNonRootNavigation`, and
`prepareRootNavigation`. Add new test groups for `prepareRootNavigation`
and the prepare-then-track interaction flow.

Co-Authored-By: Claude <noreply@anthropic.com>
Remove debug print statement from NativeAppStartIntegration, delete
obsolete FakeTimeToDisplayTrackerV2, update example app to use
structured app start spans, and apply formatting fixes.

Co-Authored-By: Claude <noreply@anthropic.com>
///
/// Returns `null` if validation fails (e.g. >60s duration, missing setup time).
@internal
AppStartInfo? parseNativeAppStart(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

pull out the common things we can to share logic with native app start handler v2 and v1

@buenaflor buenaflor marked this pull request as ready for review February 24, 2026 22:00
@buenaflor buenaflor requested a review from denrase as a code owner February 24, 2026 22:00
Copilot AI review requested due to automatic review settings February 24, 2026 22:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Ports native + generic app-start instrumentation and time-to-display navigation spans to the V2 “streaming” span API, gated by SentryTraceLifecycle.streaming, to support span-first auto-instrumentation.

Changes:

  • Adds root-navigation preparation/tracking APIs to TimeToDisplayTrackerV2 and updates navigation observer to use trackNonRootNavigation.
  • Introduces NativeAppStartHandlerV2 (streaming) and factors native app-start parsing into shared native_app_start_data.dart.
  • Extends Hub V2 span creation APIs to support startTimestamp, with updated mocks and tests.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/flutter/lib/src/navigation/time_to_display_tracker_v2.dart Adds root navigation preparation + tracking and a dedicated non-root tracking API for streaming spans.
packages/flutter/lib/src/navigation/sentry_navigator_observer.dart Switches streaming navigation tracking to trackNonRootNavigation.
packages/flutter/lib/src/integrations/native_app_start_integration.dart Routes native app-start handling to V2 handler when traceLifecycle == streaming and prepares root navigation early.
packages/flutter/lib/src/integrations/native_app_start_handler_v2.dart New streaming handler creating app-start phase spans as V2 spans under a prepared root span.
packages/flutter/lib/src/integrations/native_app_start_handler.dart Reuses shared native app-start parsing via parseNativeAppStart.
packages/flutter/lib/src/integrations/native_app_start_data.dart New shared parsing/validation types for native app-start timing.
packages/flutter/lib/src/integrations/generic_app_start_integration.dart Uses trackRootNavigation() in streaming mode instead of creating a V1 transaction.
packages/flutter/lib/src/sentry_flutter.dart Wires NativeAppStartHandlerV2 into SDK init integration setup.
packages/flutter/test/navigation/time_to_display_tracker_v2_test.dart Updates/extends tracker tests for root vs non-root navigation and preparation behavior.
packages/flutter/test/navigation/sentry_navigator_observer_test.dart Updates streaming observer tests to expect non-root navigation tracking calls.
packages/flutter/test/navigation/fake_time_to_display_tracker_v2.dart Updates fake tracker to match new API/return type.
packages/flutter/test/integrations/native_app_start_integration_test.dart Updates integration fixture to provide a V2 native app-start handler.
packages/flutter/test/integrations/native_app_start_handler_v2_test.dart New unit tests covering V2 native app-start span creation/backdating/origins.
packages/flutter/test/integrations/generic_app_start_integration_test.dart Adds streaming-mode coverage for generic app start using V2 spans.
packages/flutter/test/mocks.mocks.dart Regenerated mocks to include new startTimestamp params/defaults.
packages/flutter/example/lib/main.dart Updates example to demonstrate custom span work during app start then report fully displayed.
packages/dart/lib/src/hub.dart Adds startTimestamp support to startInactiveSpan/startIdleSpan and passes it into span constructors.
packages/dart/lib/src/hub_adapter.dart Threads startTimestamp through adapter methods.
packages/dart/lib/src/noop_hub.dart Updates NoOpHub signatures to accept startTimestamp.
packages/dart/lib/src/telemetry/span/recording_sentry_span_v2.dart Adds startTimestamp support in constructors and an internal setter for backdating.
packages/dart/lib/src/telemetry/span/idle_recording_sentry_span_v2.dart Threads startTimestamp into idle span construction.
packages/dart/test/hub_span_test.dart Adds test coverage for startTimestamp behavior on inactive + idle spans.
Comments suppressed due to low confidence (1)

packages/flutter/lib/src/navigation/time_to_display_tracker_v2.dart:183

  • The new docstring says this "cancels all spans for the current route", but the implementation cancels any active idle span on the hub (including idle spans not created by this tracker, e.g. user-interaction idle spans). Consider tightening the wording or scoping the cancellation to only spans created/prepared by this tracker.
  /// Cancels all spans for the current route and resets tracker state.
  void cancelCurrentRoute() {
    _ttfdSpan = null;
    _preparedRootNavigationSpan = null;

    final activeSpan = _hub.getActiveSpan();
    if (activeSpan is IdleRecordingSentrySpanV2) {
      activeSpan
        ..status = SentrySpanStatusV2.cancelled
        ..end();
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Remove unused `options` parameter from `parseNativeAppStart`
- Fix index mismatch bug in native span ending by ending spans inline
- Update test name and comment to reference `trackRootNavigation`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@buenaflor
Copy link
Copy Markdown
Contributor Author

@cursor review

@buenaflor
Copy link
Copy Markdown
Contributor Author

@sentry review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

buenaflor and others added 7 commits February 25, 2026 10:36
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ve switch

- Cancel orphaned idle span when handler returns early (null native start or >60s)
- Use switch on SentryTraceLifecycle for exhaustive matching instead of if/else

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Aligns constructor behavior with the setter which already calls .toUtc().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update hub span timestamp assertions to expect UTC normalization for explicit startTimestamp values on inactive and idle spans.

Co-authored-by: Cursor <cursoragent@cursor.com>
Align native app start V2 test assertions with UTC normalization so span start times are compared consistently across environments.

Co-authored-by: Cursor <cursoragent@cursor.com>

appStartSpan.end(endTimestamp: appStartEnd);

// TODO(next-pr): add mobile vitals specific attributes later
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

spec is still tbd

/// [SentryFlutter.currentDisplay] before [trackRootNavigation] fires.
/// Timestamps are backdated later in [trackRootNavigation].
void prepareRootNavigation() {
assert(_preparedRootNavigationSpan == null,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

realistically won't happen but let's assert so it shows up in debug at least as an error

Removed outdated comments regarding V1 and V2 behavior in the generic app start integration test and the native app start handler V2 test for clarity and to reflect current implementation.
@buenaflor buenaflor mentioned this pull request Feb 27, 2026
6 tasks
…AppStartHandlerV2

Streamlined the attribute assignment for span tracking by creating a single attributes map, reducing redundancy in the code. This change enhances maintainability and clarity in the NativeAppStartHandlerV2 implementation.
Added a check for null context in the NativeAppStartIntegration class to prevent potential errors. If the context is null, a warning is logged, and the integration process is skipped, enhancing robustness.
Copy link
Copy Markdown
Collaborator

@denrase denrase left a comment

Choose a reason for hiding this comment

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

Added some more context to the questions, feel free to pick up if they make sense.

Updated method names in the time-to-display tracking system to improve clarity. Changed `trackRootNavigation` to `trackAppStart` and `trackNonRootNavigation` to `trackRoute`, aligning with their functionality. Adjusted related comments and test cases to reflect these changes, enhancing code readability and maintainability.
@buenaflor buenaflor merged commit 851a2b2 into feat/span-first Mar 18, 2026
135 of 140 checks passed
@buenaflor buenaflor deleted the feat/span/native-app-start-v2 branch March 18, 2026 10:29
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.

3 participants