Skip to content

feat(tracing): Add standalone app start transaction#6359

Merged
antonis merged 13 commits into
mainfrom
antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation
Jul 2, 2026
Merged

feat(tracing): Add standalone app start transaction#6359
antonis merged 13 commits into
mainfrom
antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation

Conversation

@antonis

@antonis antonis commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

Adds an experimental, opt-in flag _experiments.enableStandaloneAppStartTracing that sends app start as a dedicated app.start transaction instead of attaching app start data to the first navigation (ui.load) transaction.

💡 Motivation and Context

Closes #5839.

💚 How did you test it?

  • Unit tests in appStart.test.ts migrated to the V2 encoding (op app.start, vitals attributes, no legacy span/measurement); breakdown-span, frame-data, and appLoaded() standalone tests updated accordingly.
  • New trace-connection test: the app.start transaction shares trace_id with the navigation transaction.
  • New defaultAppStart.test.ts: verifies the flag wires standalone on/off/default.
  • Added hermetic snapshot tests that byte-lock the full event for both flows. The default (non-opt-in) snapshot was generated against the pre-change SDK and matches unchanged after the change — proving the default path is byte-identical; the standalone snapshot guards the Span V2 encoding.
  • Added a regression test for the standalone age-check fix (slow device: app start timestamp older than MAX_APP_START_AGE_MS is still sent), with the non-standalone path still asserting the age check applies.
  • Added regression tests for the duplicate-transaction fixes: (a) deferred auto-capture fires, then appLoaded() arrives, and (b) appLoaded() races an in-flight deferred capture → in both cases only one app.start transaction is sent; verified each fails before the fix (2 transactions) and passes after.
  • Full suite green. Build, API report, lint, and circular-dep checks all pass.

📝 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

@linear-code

linear-code Bot commented Jun 26, 2026

Copy link
Copy Markdown

RN-541

@github-actions

github-actions Bot commented Jun 26, 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): 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
  • docs: Add missing 8.14.1 to changelog and SDK versions table by antonis in #6360

Plus 3 more


🤖 This preview updates automatically when you update the PR.

@github-actions

github-actions Bot commented Jun 26, 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 94e5885

@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 d5b4af2. Configure here.

Comment thread packages/core/src/js/tracing/integrations/appStart.ts
Comment thread packages/core/src/js/integrations/default.ts
antonis added a commit that referenced this pull request Jun 26, 2026
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>
antonis added a commit that referenced this pull request Jun 26, 2026
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>
Comment thread packages/core/src/js/tracing/integrations/appStart.ts
antonis added a commit that referenced this pull request Jun 26, 2026
…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>

@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 cafa472. Configure here.

antonis and others added 7 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>
@antonis antonis force-pushed the antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation branch from cafa472 to 3ddd44c Compare June 26, 2026 13:26
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jun 26, 2026
Comment thread packages/core/src/js/tracing/integrations/appStart.ts Outdated
Comment thread packages/core/src/js/tracing/integrations/appStart.ts
@sentry

sentry Bot commented Jun 26, 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 26, 2026

Copy link
Copy Markdown
Contributor

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3850.15 ms 1216.50 ms -2633.65 ms
Size 4.98 MiB 6.51 MiB 1.53 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
a50b33d+dirty 1197.74 ms 1197.17 ms -0.57 ms
0d9949d+dirty 1211.38 ms 1219.67 ms 8.29 ms
5ee78d6+dirty 3847.52 ms 1224.27 ms -2623.25 ms
100ce80+dirty 3842.93 ms 1229.52 ms -2613.41 ms
6176a94+dirty 3836.50 ms 1217.64 ms -2618.86 ms
bc0d8cf+dirty 3830.33 ms 1220.52 ms -2609.81 ms
5fe1c6c+dirty 1220.79 ms 1217.63 ms -3.16 ms
37a2091+dirty 3821.77 ms 1212.34 ms -2609.43 ms
4e0ba9c+dirty 3839.22 ms 1221.06 ms -2618.16 ms
5a23c47+dirty 3855.46 ms 1221.95 ms -2633.50 ms

App size

Revision Plain With Sentry Diff
a50b33d+dirty 3.38 MiB 4.73 MiB 1.35 MiB
0d9949d+dirty 3.38 MiB 4.76 MiB 1.38 MiB
5ee78d6+dirty 5.15 MiB 6.69 MiB 1.53 MiB
100ce80+dirty 5.15 MiB 6.67 MiB 1.51 MiB
6176a94+dirty 5.15 MiB 6.68 MiB 1.53 MiB
bc0d8cf+dirty 5.15 MiB 6.67 MiB 1.51 MiB
5fe1c6c+dirty 3.38 MiB 4.77 MiB 1.39 MiB
37a2091+dirty 5.15 MiB 6.70 MiB 1.54 MiB
4e0ba9c+dirty 5.15 MiB 6.67 MiB 1.51 MiB
5a23c47+dirty 4.98 MiB 6.46 MiB 1.49 MiB

Previous results on branch: antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation

Startup times

Revision Plain With Sentry Diff
bc395fb+dirty 3832.21 ms 1216.71 ms -2615.50 ms

App size

Revision Plain With Sentry Diff
bc395fb+dirty 4.98 MiB 6.51 MiB 1.53 MiB

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 440.78 ms 446.15 ms 5.36 ms
Size 49.74 MiB 55.09 MiB 5.34 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
a858ac3+dirty 426.50 ms 484.82 ms 58.32 ms
4e0ba9c+dirty 452.84 ms 473.36 ms 20.52 ms
0a147b2+dirty 464.71 ms 538.81 ms 74.09 ms
9474ead+dirty 411.45 ms 446.80 ms 35.35 ms
7887847+dirty 416.61 ms 462.04 ms 45.43 ms
5a21b51+dirty 471.42 ms 524.22 ms 52.80 ms
4966363+dirty 400.04 ms 431.08 ms 31.04 ms
1a2e7e0+dirty 416.61 ms 445.46 ms 28.85 ms
f170ec3+dirty 428.71 ms 452.18 ms 23.47 ms
a50b33d+dirty 500.81 ms 532.11 ms 31.30 ms

App size

Revision Plain With Sentry Diff
a858ac3+dirty 49.74 MiB 55.08 MiB 5.34 MiB
4e0ba9c+dirty 48.30 MiB 53.49 MiB 5.19 MiB
0a147b2+dirty 49.74 MiB 55.08 MiB 5.34 MiB
9474ead+dirty 48.30 MiB 53.61 MiB 5.30 MiB
7887847+dirty 49.74 MiB 54.81 MiB 5.07 MiB
5a21b51+dirty 48.30 MiB 53.49 MiB 5.19 MiB
4966363+dirty 48.30 MiB 53.54 MiB 5.24 MiB
1a2e7e0+dirty 49.74 MiB 54.82 MiB 5.07 MiB
f170ec3+dirty 48.30 MiB 53.57 MiB 5.26 MiB
a50b33d+dirty 43.75 MiB 48.08 MiB 4.33 MiB

Previous results on branch: antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation

Startup times

Revision Plain With Sentry Diff
bc395fb+dirty 465.54 ms 549.85 ms 84.31 ms

App size

Revision Plain With Sentry Diff
bc395fb+dirty 49.74 MiB 54.85 MiB 5.11 MiB

Comment thread packages/core/src/js/tracing/integrations/appStart.ts Outdated
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 3845.11 ms 1229.57 ms -2615.54 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 3860.14 ms 1223.39 ms -2636.76 ms
a5d243c+dirty 3827.92 ms 1220.10 ms -2607.81 ms
5c1e987+dirty 1208.43 ms 1220.72 ms 12.29 ms
b9bebee+dirty 3858.02 ms 1231.92 ms -2626.11 ms
853723c+dirty 3849.33 ms 1221.07 ms -2628.26 ms
c151573+dirty 3835.64 ms 1216.10 ms -2619.53 ms
1a2e7e0+dirty 3852.95 ms 1233.78 ms -2619.18 ms
7d6fd3a+dirty 1210.89 ms 1217.63 ms 6.74 ms
a3265b6+dirty 3844.26 ms 1235.60 ms -2608.66 ms
df5d108+dirty 1207.34 ms 1210.50 ms 3.16 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

Previous results on branch: antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation

Startup times

Revision Plain With Sentry Diff
bc395fb+dirty 3838.67 ms 1213.69 ms -2624.98 ms

App size

Revision Plain With Sentry Diff
bc395fb+dirty 4.98 MiB 6.50 MiB 1.52 MiB

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 407.36 ms 441.70 ms 34.34 ms
Size 49.74 MiB 55.08 MiB 5.34 MiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
3d377b5+dirty 425.38 ms 440.67 ms 15.30 ms
a3265b6+dirty 410.96 ms 444.76 ms 33.80 ms
20fbd51+dirty 594.38 ms 655.35 ms 60.97 ms
5ee78d6+dirty 411.18 ms 437.83 ms 26.65 ms
7a89652+dirty 405.00 ms 437.04 ms 32.04 ms
09a902f+dirty 423.02 ms 472.18 ms 49.16 ms
68ae91b+dirty 515.04 ms 578.08 ms 63.04 ms
0d9949d+dirty 414.88 ms 428.68 ms 13.81 ms
4b87b12+dirty 356.23 ms 399.86 ms 43.63 ms
71abba0+dirty 411.04 ms 453.67 ms 42.63 ms

App size

Revision Plain With Sentry Diff
3d377b5+dirty 43.94 MiB 49.00 MiB 5.06 MiB
a3265b6+dirty 48.30 MiB 53.58 MiB 5.28 MiB
20fbd51+dirty 49.74 MiB 54.81 MiB 5.07 MiB
5ee78d6+dirty 48.30 MiB 53.58 MiB 5.28 MiB
7a89652+dirty 48.30 MiB 53.60 MiB 5.30 MiB
09a902f+dirty 49.74 MiB 54.81 MiB 5.07 MiB
68ae91b+dirty 49.74 MiB 54.79 MiB 5.05 MiB
0d9949d+dirty 43.94 MiB 48.99 MiB 5.05 MiB
4b87b12+dirty 43.94 MiB 49.00 MiB 5.06 MiB
71abba0+dirty 48.30 MiB 53.49 MiB 5.19 MiB

Previous results on branch: antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation

Startup times

Revision Plain With Sentry Diff
bc395fb+dirty 418.21 ms 464.18 ms 45.97 ms

App size

Revision Plain With Sentry Diff
bc395fb+dirty 49.74 MiB 54.85 MiB 5.11 MiB

@antonis antonis removed the ready-to-merge Triggers the full CI test suite label Jun 29, 2026
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>
@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jun 29, 2026
@antonis

antonis commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@sentry review

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

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

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1e78698. Configure here.

Comment thread packages/core/src/js/tracing/integrations/appStart.ts
@antonis antonis marked this pull request as ready for review June 29, 2026 08:28
@antonis antonis removed the ready-to-merge Triggers the full CI test suite label Jun 29, 2026
Comment thread packages/core/src/js/tracing/integrations/appStart.ts
…eattracing-decouple-app-start-data-from-navigation

@lucas-zimerman lucas-zimerman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

@antonis antonis added the ready-to-merge Triggers the full CI test suite label Jul 2, 2026
Comment thread packages/core/src/js/tracing/integrations/appStart.ts
@antonis antonis merged commit 40c9884 into main Jul 2, 2026
112 of 124 checks passed
@antonis antonis deleted the antonislilis/rn-541-feattracing-decouple-app-start-data-from-navigation branch July 2, 2026 08:45
antonis added a commit that referenced this pull request Jul 2, 2026
…6369)

* feat(tracing): Add standalone app start transaction (RN-541)

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>

* docs: Link PR in changelog entry

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

* test(tracing): Lock app start event shape with snapshots

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>

* fix(tracing): Skip app start age check for standalone transactions

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>

* fix(tracing): Prevent duplicate standalone app start transactions

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>

* fix(tracing): Guard against racing in-flight standalone app start capture

`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>

* refactor(tracing): Tidy standalone app start implementation

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

* refactor(tracing): Simplify standalone app start dedup to a single guard

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>

* feat(tracing): Emit app.vitals.start.screen on standalone app start (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>

* docs: Add changelog entry for app.vitals.start.screen

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

feat(tracing): Decouple app start data from navigation transaction lifecycle

2 participants