Skip to content

feat(tracing): Emit app.vitals.start.screen on standalone app start#6369

Merged
antonis merged 18 commits into
mainfrom
antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start
Jul 2, 2026
Merged

feat(tracing): Emit app.vitals.start.screen on standalone app start#6369
antonis merged 18 commits into
mainfrom
antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start

Conversation

@antonis

@antonis antonis commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

Sets the app.vitals.start.screen attribute on the standalone app.start transaction 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

  • 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.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

antonis and others added 10 commits June 26, 2026 15:19
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>
…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>
@linear-code

linear-code Bot commented Jun 29, 2026

Copy link
Copy Markdown

RN-691

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • feat(tracing): Emit app.vitals.start.screen on standalone app start by antonis in #6369
  • feat(tracing): Add standalone app start transaction by antonis in #6359
  • chore(deps): update Cocoa SDK to v9.19.1 by github-actions in #6389
  • chore: Add PR template checkbox for cross sdk review on public API changes by antonis in #6386
  • fix(e2e): Remove dead OSSRH snapshots repo from RN 0.71 Gradle plugin by antonis in #6387
  • chore(deps): update JavaScript SDK to v10.63.0 by github-actions in #6388
  • chore(e2e): Update Expo sample to SDK 57 by antonis in #6385
  • chore: Auto-request mobile team review on public API changes by antonis in #6383
  • fix(replay): Forward Session Replay network detail options to native SDKs by antonis in #6373
  • fix(ci): Pin Android E2E emulator to build 15679343 (37.1.5.0) by antonis in #6378
  • feat(core): Expose top-level Sentry.setAttribute / setAttributes by antonis in #6354
  • docs: Add AI Use section to CONTRIBUTING.md by christophaigner in #6374
  • feat(replay): Default networkCaptureBodies to true by alwx in #6372
  • chore(deps): bump getsentry/craft from 2.26.10 to 2.26.13 by dependabot in #6368
  • chore(deps): bump getsentry/github-workflows/danger from 17cc15eb58ea3687cd8f2714a4192dcee4aa09ef to 4013fc6e1aeb1be1f9d3b4d232624f0ec1afa613 by dependabot in #6366
  • chore(deps): bump getsentry/github-workflows/validate-pr from 71588ddf95134f804e82c5970a8098588e2eaecd to 4013fc6e1aeb1be1f9d3b4d232624f0ec1afa613 by dependabot in #6364
  • chore(deps): bump getsentry/craft/.github/workflows/changelog-preview.yml from 2.26.10 to 2.26.13 by dependabot in #6367
  • feat(core): Wire TurboModulePerfLogger on iOS and Android by alwx in #6307
  • chore(deps): bump actions/cache from 4 to 6 by dependabot in #6365
  • chore(deps): update CLI to v3.6.0 by github-actions in #6362
  • chore(deps): bump faraday from 1.10.5 to 1.10.6 in /samples/react-native by dependabot in #6363
  • chore(deps): update JavaScript SDK to v10.62.0 by github-actions in #6361
  • Expo Router ErrorBoundary auto wrapped by alwx in #6347
  • chore(ci): Move sample app iOS build jobs to GitHub Actions runners by itaybre in #6356

Plus 4 more


🤖 This preview updates automatically when you update the PR.

@github-actions

github-actions Bot commented Jun 29, 2026

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

Generated by 🚫 dangerJS against d3fd09d

@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jun 29, 2026

@cursor cursor 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.

✅ 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.

@sentry

sentry Bot commented Jun 29, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.16.0 (94) Release

⚙️ sentry-react-native Build Distribution Settings

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3848.18 ms 1213.62 ms -2634.56 ms
Size 4.98 MiB 6.51 MiB 1.53 MiB

Baseline results on branch: main

Startup times

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

@antonis antonis removed the ready-to-merge Triggers the full CI test suite label Jun 29, 2026
…-from-navigation' into antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start
@antonis antonis marked this pull request as ready for review June 29, 2026 09:37
antonis added 4 commits June 30, 2026 14:39
…-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
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jul 2, 2026
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3821.24 ms 1213.23 ms -2608.01 ms
Size 4.98 MiB 6.51 MiB 1.53 MiB

Baseline results on branch: main

Startup times

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

@antonis antonis merged commit bfba737 into main Jul 2, 2026
116 of 124 checks passed
@antonis antonis deleted the antonislilis/rn-691-emit-appvitalsstartscreen-on-the-standalone-app-start branch July 2, 2026 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants