feat(tracing): Emit app.vitals.start.screen on standalone app start#6369
Merged
antonis merged 18 commits intoJul 2, 2026
Merged
Conversation
Add experimental `_experiments.enableStandaloneAppStartTracing` to send app start as a dedicated `app.start` transaction (Span V2) instead of attaching app start data to the first navigation (`ui.load`) transaction. The standalone transaction uses op `app.start`, name `App Start`, and carries the vitals as attributes on the root span (`app.vitals.start.value`, `app.vitals.start.type`). This decouples app start from the navigation transaction lifecycle, so it is no longer lost when no qualifying navigation transaction is sent. The legacy (non-standalone) path is unchanged and remains the default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add hermetic snapshot tests that byte-lock the full event for both the default (non-standalone) and opt-in standalone cold app start flows. The default snapshot was generated against the pre-change SDK and matches unchanged after the standalone changes, proving the non-opt-in path is unaffected. The standalone snapshot guards the Span V2 encoding (op `app.start`, `app.vitals.start.*`, no legacy per-type span/measurement). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The MAX_APP_START_AGE_MS bounds check compared the native app start time against `event.start_timestamp`, which for a standalone transaction still holds the span creation time at that point (it is corrected to the native app start time later). On slow devices that gap can exceed the threshold and discard a valid app start. The age check only makes sense for the non-standalone path (where app start is attached to a later navigation transaction), so skip it for standalone; the duration check still filters genuinely bogus app starts. Surfaced by automated review on #6359. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In standalone mode the deferred auto-capture (`setTimeout(0)`) is meant to be cancelled by a later `appLoaded()` call. But once the deferred send has fired, `cancelDeferredStandaloneCapture()` is a no-op and `_appLoaded()` reset the flushed flag and sent a second `app.start` transaction for the same app run (the common case, since apps signal readiness in a later macrotask). Track whether a standalone transaction has already been sent and skip re-sending; reset the flag on `runApplication` so a new app run can send again. Surfaced by automated review on #6359. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ture `captureStandaloneAppStart` awaits native work before it marks the transaction as sent, so a late `appLoaded()` racing an in-flight deferred capture could pass the sent-guard and emit a second `app.start` transaction. Add an in-flight flag set synchronously at entry (no await before it, so the check-and-set is atomic in JS) and cleared in a `finally`, alongside the existing already-sent guard. Surfaced by automated review on #6359. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Trim the synthetic breakdown-parent to the fields the span helpers actually read (op/origin/span_id/trace_id/start_timestamp/data); drop the inert ones. - Gate the carrier-transaction end-timestamp clamp to non-standalone, since the standalone path sets its end timestamp explicitly. No behavior change — the default and standalone event snapshots are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the `standaloneAppStartSent` + `standaloneCaptureInProgress` pair (and the try/finally) with a single `standaloneAppStartCaptured` flag, set synchronously at the start of `captureStandaloneAppStart` before any await. Being set atomically makes it both the once-per-run guard and the in-flight guard, so a late or racing trigger for the same run bails cleanly. It is reset on runApplication so the next app run captures again. Also remove the now-vestigial `resetAppStartDataFlushed()`: appLoaded() overrides the end timestamp by cancelling the deferred send before it fires; once the deferred has fired the transaction is already sent, so re-sending would duplicate. The reset call only tried (and failed) to enable that duplicate send. Cross-run dedup continues to rely on the native `has_fetched` flag. Addresses review findings on #6359. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-start-data-from-navigation
…RN-691) Set the `app.vitals.start.screen` attribute on the standalone `app.start` transaction from the current route tracked by the tracing integration. Unlike the non-standalone `ui.load` transaction (named after the screen, which Relay backfills from), the standalone transaction is named `App Start`, so the screen is set explicitly. Omitted when no route has been registered yet at capture time. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contributor
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
Plus 4 more 🤖 This preview updates automatically when you update the PR. |
Contributor
|
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 77f2b5d. Configure here.
📲 Install BuildsAndroid
|
Contributor
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 267d3ed+dirty | 3853.65 ms | 1224.70 ms | -2628.94 ms |
| a5d243c+dirty | 3842.35 ms | 1214.29 ms | -2628.06 ms |
| 5c1e987+dirty | 1204.30 ms | 1222.15 ms | 17.85 ms |
| b9bebee+dirty | 3850.15 ms | 1227.51 ms | -2622.64 ms |
| 853723c+dirty | 3852.60 ms | 1234.64 ms | -2617.96 ms |
| c151573+dirty | 3841.06 ms | 1232.13 ms | -2608.93 ms |
| 1a2e7e0+dirty | 3842.49 ms | 1220.04 ms | -2622.45 ms |
| 7d6fd3a+dirty | 1223.29 ms | 1229.57 ms | 6.28 ms |
| a3265b6+dirty | 3826.31 ms | 1207.87 ms | -2618.44 ms |
| df5d108+dirty | 1225.90 ms | 1220.14 ms | -5.76 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 267d3ed+dirty | 5.15 MiB | 6.69 MiB | 1.54 MiB |
| a5d243c+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 5c1e987+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| b9bebee+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 853723c+dirty | 5.15 MiB | 6.69 MiB | 1.53 MiB |
| c151573+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 1a2e7e0+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 7d6fd3a+dirty | 3.38 MiB | 4.77 MiB | 1.39 MiB |
| a3265b6+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| df5d108+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
…-start-data-from-navigation
…-from-navigation' into antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start
alwx
approved these changes
Jun 29, 2026
…-start-data-from-navigation
…-from-navigation' into antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start
…eattracing-decouple-app-start-data-from-navigation
…-from-navigation' into antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start
Base automatically changed from
antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation
to
main
July 2, 2026 08:45
…mit-appvitalsstartscreen-on-the-standalone-app-start # Conflicts: # CHANGELOG.md # packages/core/src/js/tracing/integrations/appStart.ts # packages/core/src/js/tracing/semanticAttributes.ts # packages/core/test/tracing/integrations/appStart.test.ts
Contributor
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5c1e987+dirty | 1208.43 ms | 1220.72 ms | 12.29 ms |
| 4e0b819+dirty | 3828.96 ms | 1205.64 ms | -2623.32 ms |
| ecf47a2+dirty | 3844.68 ms | 1223.98 ms | -2620.70 ms |
| 4953e94+dirty | 1217.41 ms | 1223.53 ms | 6.12 ms |
| 5a010b7+dirty | 3856.76 ms | 1232.65 ms | -2624.11 ms |
| 44c8b3f+dirty | 3849.24 ms | 1209.94 ms | -2639.31 ms |
| 27d9693+dirty | 3837.40 ms | 1227.74 ms | -2609.67 ms |
| c2e182c+dirty | 3841.76 ms | 1220.76 ms | -2621.01 ms |
| 4966363+dirty | 3863.07 ms | 1227.19 ms | -2635.88 ms |
| 3a829f0+dirty | 3822.15 ms | 1211.04 ms | -2611.10 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5c1e987+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 4e0b819+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| ecf47a2+dirty | 4.98 MiB | 6.46 MiB | 1.49 MiB |
| 4953e94+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 5a010b7+dirty | 5.15 MiB | 6.69 MiB | 1.54 MiB |
| 44c8b3f+dirty | 5.15 MiB | 6.66 MiB | 1.51 MiB |
| 27d9693+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| c2e182c+dirty | 4.98 MiB | 6.51 MiB | 1.53 MiB |
| 4966363+dirty | 5.15 MiB | 6.68 MiB | 1.53 MiB |
| 3a829f0+dirty | 5.15 MiB | 6.70 MiB | 1.54 MiB |
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📢 Type of change
📜 Description
Sets the
app.vitals.start.screenattribute on the standaloneapp.starttransaction introduced in #6359, sourced from the current route tracked by the React Native tracing integration.💡 Motivation and Context
Closes RN-691
💚 How did you test it?
CI, Manual
📝 Checklist
sendDefaultPIIis enabled🔮 Next steps