Skip to content

feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS MetricKit app-exit telemetry#72

Merged
gmaclennan merged 1 commit into
mainfrom
sentry-exit-telemetry
Jun 10, 2026
Merged

feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS MetricKit app-exit telemetry#72
gmaclennan merged 1 commit into
mainfrom
sentry-exit-telemetry

Conversation

@gmaclennan

Copy link
Copy Markdown
Member

Post-mortem visibility on OS-driven kills, answering "does our backend stay alive long enough on this user's device?" per platform.

Android (Phase 6)

ExitReasonsCollector surfaces ActivityManager.getHistoricalProcessExitReasons() records (API 30+) as "android exit: REASON_*" events on the next process start:

  • Two callers, each reporting its own process only: the main process via a new ComapeoCoreApplicationLifecycleListener, the FGS from ComapeoCoreService.onCreate. Both run off the main thread, and collection runs before the current run's anchors are stamped so the decoder sees the previous session's values.
  • Per-process high-water timestamp dedupes across runs. First observation initialises it to "now" and emits nothing, so a device's first update never floods Sentry with the pre-feature backlog.
  • oem.killer.suspected=true tags the SIGKILL-to-foreground(-service) signature of OEM custom killers reaching past FGS protection — the headline cohort, sliceable by manufacturer/model.
  • exit.intentional separates user force-stops from system kills so kill-rate dashboards can exclude them.
  • Wall-clock anchors (BackgroundAnchors, stored in the shared prefs file so the FGS can read the main process's background state; ProcessLifecycleOwner ON_STOP/ON_START maintains backgrounded_at) derive uptime_bucket / bg_duration_bucket coarse tags + exact-ms extras, and comapeo.fgs.killed_in_background.
  • Pre-API-30 devices set a one-time exitReasons.supported=false scope tag and no-op.

iOS (Phase 7a)

AppExitMetricsCollector subscribes to MXMetricPayload — the MetricKit metric side sentry-cocoa deliberately skips (their SentryMetricKitIntegration only covers the diagnostic side) — and forwards MXAppExitMetric buckets as "ios exit: <cohort>_<bucket>" events with exit.cause_class / exit.intentional tags and a stable window_id. The decode logic (AppExitDecoder) is MetricKit-free, so it compiles and is unit-tested on macOS via swift test; the MXMetricManagerSubscriber adapter is #if os(iOS).

Phase 9b.8 tier reclassification (shipped here, not deferred)

The duration-derived Android fields and the iOS per-exit duplication only flow when captureApplicationData is on; the records themselves ship at the diagnostic tier.

Divergences from plan

  • Pre-iOS-14 appExitMetrics.supported=false branch dropped — the podspec floor is iOS 15.1, so it was dead code.
  • One emission helper per platform (Sentry.captureEvent on Android serves both processes; SentryNativeBridge.captureMessage gained an extras param) — deliberately a single seam for the planned migration to Sentry Application Metrics once the native pins reach the metrics floors (sentry-android ≥ 8.34, sentry-cocoa ≥ 9.12); see the Phase 8 note in the plan.

Tests & validation

  • 25 new JVM tests (high-water behaviour, tag/level/tier mapping, bucket boundary cases, decode-table coverage incl. unknown-int fallthrough).
  • 11 new Swift tests (per-exit duplication semantics, tier gating, cause-class/level mapping, unknown-bucket fallthrough, window-id stability).
  • Validated live on an API 34 emulator: SIGKILLed the FGS while foreground-service (the OEM-killer signature) and the main process while backgrounded; each kill surfaced exactly once on the next start with the high-water mark advancing to the exact record timestamp. Pre-30 fallback probed on an API 29 emulator (clean no-op). iPhone 16 simulator confirmed the MetricKit subscription at launch (payload delivery isn't testable in simulator/TestFlight — App Store builds only).
  • Empirically discovered + documented: the system persists exit records asynchronously, so a START_STICKY restart ~2s after a kill can see nothing (record surfaces next start), and in a rapid kill→restart burst the first record can be skipped permanently. Added to the §7.5.1 caveats.

Phase 7b (UserDefaults killed-in-background heuristic) stays in the plan as optional follow-up.

🤖 Generated with Claude Code

…it app-exit telemetry

Post-mortem visibility on OS-driven kills, answering "does our backend
stay alive long enough on this user's device?" per platform.

Android (Phase 6): ExitReasonsCollector surfaces
getHistoricalProcessExitReasons records (API 30+) as
"android exit: REASON_*" events on the next process start — main process
via a new ApplicationLifecycleListener, FGS from
ComapeoCoreService.onCreate, each reporting its own process only. A
per-process high-water timestamp dedupes across runs (first observation
initialises it and emits nothing, so device updates never flood Sentry
with the pre-feature backlog). oem.killer.suspected tags the
SIGKILL-to-foreground(-service) signature of OEM custom killers.
Wall-clock anchors (BackgroundAnchors, ProcessLifecycleOwner
ON_STOP/ON_START) derive uptime/backgrounded-for buckets. Pre-30 devices
set a one-time exitReasons.supported=false scope tag.

iOS (Phase 7a): AppExitMetricsCollector subscribes to MXMetricPayload —
the MetricKit metric side sentry-cocoa deliberately skips — and forwards
MXAppExitMetric buckets as "ios exit: <cohort>_<bucket>" events with
cause-class/intentional tags and a stable window_id. The decode logic
(AppExitDecoder) is MetricKit-free so it compiles and tests on macOS via
swift test.

Phase 9b.8's tier reclassification ships here rather than later: the
duration-derived Android fields and the iOS per-exit duplication only
flow when captureApplicationData is on; the records themselves are
diagnostic-tier.

Divergence from plan: the pre-iOS-14 supported=false branch was dropped
(podspec floor is 15.1 — dead code). Phase 7b (UserDefaults
killed-in-background heuristic) stays in the plan as optional follow-up.

Tests: 25 new JVM tests (high-water, tag/level mapping, tier gating,
bucket boundaries, decode tables), 11 new Swift tests (duplication
semantics, tier gating, unknown-bucket fallthrough). MetricKit usage
typechecked against the iOS SDK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gmaclennan gmaclennan merged commit caef2e4 into main Jun 10, 2026
7 checks passed
@gmaclennan gmaclennan deleted the sentry-exit-telemetry branch June 10, 2026 10:38
gmaclennan added a commit that referenced this pull request Jun 10, 2026
…bering (#84)

Post-merge review fixes for the Phase 6/7a exit-reason telemetry (#72).
A code review after the merge found a cluster of correctness bugs in the
collection pipeline — three of which undermined the headline metrics the
feature exists to produce.

## Android

**Records were silently consumed without being reported.** Three paths
shared one root cause: `collect()` advanced the `last_seen` high-water
mark *before* capture, and capture silently no-ops on an uninitialised
Sentry hub.

- The main process collected at `Application.onCreate`, but main-process
sentry-android only comes up when JS-side `Sentry.init` runs (manifest
sets `io.sentry.auto-init=false`) — so main-process exit records were
dropped on essentially every cold start.
- The FGS ran the collector even with `diagnosticsEnabled=false`,
consuming records a user opting in later could never see.

Fix: collection no-ops (leaving records pending) until
`Sentry.isEnabled()`, the main process waits up to 2 minutes for the
JS-triggered init, and the high-water mark advances only after the
capture loop (at-least-once; duplicates absorbed by the stable message
grouping). Both callers snapshot the previous session's anchors
(`AnchorSnapshot`) before stamping their own, so collection can run
arbitrarily late without reading current-run values.

**`comapeo.fgs.killed_in_background` was systematically `false`** in the
dominant kill→relaunch flow: the relaunch's replayed `ON_START` zeroed
`backgrounded_at` before the FGS (started only after foregrounding) read
it. Fix: `ON_START` now stamps a paired `foregrounded_at` anchor instead
of clearing; the decoder treats an exit as in-background when the last
stamp before its timestamp was a background. Neither stamp is ever
cleared, so the answer is correct however late collection runs.

**Cross-process SharedPreferences clobbering.** Both processes wrote the
same prefs file, and SharedPreferences rewrites the whole file from a
per-process cache — one process's write could revert or erase the
other's anchors, high-water marks, or even the Sentry toggles. Fix: one
anchors file per process (`com.comapeo.core.anchors.<proc>`) with
exactly one writer each; the FGS reads the `main` file read-only. Writes
use `commit=true` so the `backgrounded_at` stamp survives a SIGKILL
right after backgrounding (the exact window this feature measures).
Note: the file move resets the high-water mark once — the first run on
this code re-initialises it and emits nothing, then reporting resumes.

**Shared record window.** `getHistoricalProcessExitReasons(pkg, 0, 10)`
returns the newest 10 records across *all* processes of the package, so
FGS churn (START_STICKY restart loops) could evict main-process death
records before they were ever seen. Fix: query without a cap (the OS
bounds retention at ~16 per package) and keep the newest 10 per process
after filtering.

Smaller cleanups: `ExitReasonTags.levelFor` returns `SentryLevel`
directly (was a string re-parsed by a second when-block that silently
degraded unknown values to INFO), and the FGS process name is derived at
runtime via a shared helper instead of a literal copy of the manifest's
`android:process` — a rename can no longer silently break the record
filter.

## iOS

- **Double-counted foreground OOM/watchdog deaths**: sentry-cocoa's
watchdog-termination tracking (enabled by default) already reports these
as errors, so the foreground `memory_resource_limit` / `app_watchdog`
MetricKit buckets are demoted to warning — same policy the crash buckets
already followed. Background buckets stay error (the SDK heuristic only
covers foreground deaths).
- **Unbounded event bursts**: app-usage-tier per-exit duplication is now
capped at 10 events per window+bucket — counts are 24h cumulative totals
and benign buckets (background `normal_app_exit`) reach the hundreds,
burning quota in a burst on every daily payload. `window_count` keeps
the exact count; query `sum(window_count)` when exactness matters.

Docs (`docs/sentry-integration.md` §7.5.1/§7.5.2) updated to match.

## Tests

- Android JVM: 27 pass, including new coverage for the high-water
return-don't-persist contract, the per-process cap, and the
fg/bg-stamp-vs-exit-timestamp ordering (incl. the relaunch regression
case).
- iOS package: 103 pass, including new coverage for the per-cohort level
split and the duplication cap.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
gmaclennan added a commit that referenced this pull request Jun 10, 2026
* origin/main:
  fix(sentry): make exit telemetry lossless and stop cross-process clobbering (#84)
  feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS MetricKit app-exit telemetry (#72)
  chore(build): use npm list instead of custom traversal to get native module versions (#70)
gmaclennan added a commit that referenced this pull request Jun 11, 2026
…pplication Metrics (#73)

Stacked on #72 (merge that first; this PR's base is
`sentry-exit-telemetry`).

Sentry's Application Metrics (count/gauge/distribution with attributes;
no trace context required; no issue lifecycle) is the right primitive
for stats-gathering telemetry like the exit reasons landed in #72 —
events there create perpetually-"unresolved" issues and regression-alert
noise. `@sentry/react-native` v8 lock-steps native SDKs that have the
metrics API (sentry-android 8.43, sentry-cocoa 9.15), so this PR does
the migration and the flip together.

## SDK migration

- **peer + dev dep** `@sentry/react-native` `^7.13.0` → `^8.13.0` — ⚠️
**breaking for consumers**: the host app must upgrade its Sentry SDK in
the same release (v8 floors: iOS 15+, Xcode 16.4+, AGP 7.4+, Kotlin 1.8+
— all already satisfied by this module/Expo 55).
- **iOS**: `Sentry/HybridSDK '8.58.0'` → `Sentry '9.15.0'` in the
podspec (cocoa 9 dropped the `HybridSDK` subspec; RNSentry v8 depends on
the plain pod), matching `exact:` pin in `Package.swift`.
`check-sentry-cocoa-pin.mjs` updated for RN v8's podspec shape (plain
`Sentry` dep, version via the `sentry_cocoa_version` Ruby variable).
- **Android**: `sentry-android-core` 8.32.0 → 8.43.0.
- **cocoa 9 API drift**: exactly one call site (`TransactionContext`
gained nullable `sampleRate`/`sampleRand`). The `@_spi(Private)`
surfaces the bridge relies on (`SentryEventDecoder`,
`PrivateSentrySDKOnly`) survive the major unchanged.

## Exit telemetry → `comapeo.app.exit` count metric

- **Android**: one count per record via `Sentry.metrics().count`, former
tags+extras as attributes; event level becomes the `exit.severity`
attribute (`error`/`warning`/`info`). Same high-water/anchor pipeline
and tests.
- **iOS**: one count per window+bucket with the cumulative value as the
metric value — the per-exit event duplication and its `window_id` dedup
machinery are deleted; a count carries N natively.
- Both SDKs ship metrics enabled by default
(`enableMetrics`/`Metrics.enable` default true), so no init changes.

## Privacy re-tiering — metrics need fewer opt-ins

Metrics are aggregate and low-cardinality (pre-bucketed tags, no
per-user timeline, no free text), so coarse fields moved to the
always-on diagnostic tier:

- Android: `uptime_bucket`, `bg_duration_bucket`,
`comapeo.fgs.killed_in_background` now flow at diagnostic; exact
`alive_for_ms` / `backgrounded_for_ms` remain behind
`captureApplicationData`.
- iOS: nothing left gated — the count-per-window shape has no usage-tier
residue.

The plan is reshaped on the same principle:

- Phase 5's per-RPC spans, backend memory checkpoint, and storage-size
sample re-tier into Phase 11's **diagnostic metrics inventory** — RPC
*timing* becomes `comapeo.rpc.*.duration_ms` distribution metrics at
diagnostic; per-RPC *traces* (the privacy-expensive shape) move behind
the `debug` toggle.
- Phase 11 marked **unblocked**: every layer now has the metrics API,
including the backend (`@sentry/node-core` 10.53 ≥ the 10.25 floor), and
`comapeo.app.exit` proves the native emission path.

## Validation

- `swift test`: 99 tests, 0 failures against cocoa 9.15; MetricKit half
typechecked against the iOS 15.1 SDK.
- Gradle JVM unit tests green against sentry-android 8.43.
- `npm run build` + `lint` green; example app typechecks against v8
(`appStartIntegration` survives).
- `check-sentry-cocoa-pin.mjs` passes with the v8 layout.
- **Runtime** (API 34 emulator, example app rebuilt on the new SDKs):
backend boots to STARTED; SIGKILLed the FGS while foreground-service and
the relaunch reported the record through `Sentry.metrics().count` with
no errors logged.

Consumer note for the example/dev workflow: after pulling this branch,
re-run `pod install` in `apps/example/ios` (new pod major + new source
file list).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
gmaclennan added a commit that referenced this pull request Jun 16, 2026
* origin/main:
  chore(e2e): add e2e tests on browserstack via Maestro (#56)
  fix(sentry): make exit telemetry lossless and stop cross-process clobbering (#84)
  feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS MetricKit app-exit telemetry (#72)
  chore(build): use npm list instead of custom traversal to get native module versions (#70)
gmaclennan added a commit that referenced this pull request Jun 22, 2026
## Optic Release Automation

This **draft** PR is opened by Github action
[optic-release-automation-action](https://github.com/nearform-actions/optic-release-automation-action).

A new **draft** GitHub release
[v1.0.0-pre.2](https://github.com/digidem/comapeo-core-react-native/releases/tag/untagged-c499977757c9745e56b2)
has been created.

Release author: @gmaclennan

#### If you want to go ahead with the release, please merge this PR.
When you merge:

- The GitHub release will be published

- The npm package with tag pre will be published according to the
publishing rules you have configured



- No major or minor tags will be updated as configured


#### If you close the PR

- The new draft release will be deleted and nothing will change

## What's Changed
* Android Testing Infrastructure & Bug Fixes by @gmaclennan in
#3
* chore: prebuild example/android; harden instrumented tests by
@gmaclennan in
#10
* Integrate @comapeo/core via IPC over Unix sockets by @gmaclennan in
#5
* chore: adjust repo setup by @achou11 in
#12
* chore: minor fixes based on expo-doctor by @achou11 in
#13
* Add iOS support & test infrastructure by @gmaclennan in
#6
* chore: add architecture docs & plans by @gmaclennan in
#11
* update some native deps used in backend by @achou11 in
#14
* iOS Phase 1: unified JS bundle + smoke test (simulator-only) by
@gmaclennan in
#15
* iOS Phase 2: xcframework Embed & Sign for native addons by @gmaclennan
in #16
* Phase 2 Android: jniLibs packaging + unified rollup loader plugin by
@gmaclennan in
#17
* chore: post-Phase-2 cleanup — comments, plan docs, agents.md by
@gmaclennan in
#33
* android: read abiFilters from reactNativeArchitectures (#30) by
@gmaclennan in
#35
* refactor: simplify build-backend.ts; rollup writes directly to native
asset trees by @gmaclennan in
#34
* chore: fix eslint configuration by @achou11 in
#41
* android: audit 16 KB page alignment on every shipped .so by
@gmaclennan in
#43
* Add rootkey persistence and lifecycle state management by @gmaclennan
in #36
* chore: move example app into apps directory by @achou11 in
#18
* refactor: per-component lifecycle state with derived ComapeoState by
@gmaclennan in
#47
* android: fold waitForFile into connect retry loop by @gmaclennan in
#52
* chore: add e2e testing app by @achou11 in
#49
* fix(android): drop setUnlockedDeviceRequired from rootkey wrapper key
by @gmaclennan in
#57
* fix(backend): cache stopping/error frames for late joiners by
@gmaclennan in
#58
* fix(ios-tests): wait for STOPPING before signalling node exit by
@gmaclennan in
#59
* fix(android): drain JNI stdio pumps before returning from node::Start
by @gmaclennan in
#60
* Sentry integration: Phase 1 + Phase 2a + Phase 2b by @gmaclennan in
#54
* feat(backend): polywasm-backed undici on iOS, re-enable maps plugin by
@gmaclennan in
#62
* ci: drop unreliable Android emulator snapshot caching by @gmaclennan
in #64
* feat(sentry): land Phase 3 — backend loader + RPC tracing by
@gmaclennan in
#63
* fix(ios-tests): serialise STOPPING/STOPPED observers in
testFullLifecycleStateTransitions by @gmaclennan in
#71
* use npm list instead of custom traversal to get native module versions
by @achou11 in
#70
* feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS
MetricKit app-exit telemetry by @gmaclennan in
#72
* fix(sentry): make exit telemetry lossless and stop cross-process
clobbering by @gmaclennan in
#84
* chore(e2e): add e2e tests on browserstack via Maestro by @achou11 in
#56
* feat(sentry): migrate to @sentry/react-native v8; exit telemetry as
Application Metrics by @gmaclennan in
#73
* Map server integration by @gmaclennan in
#86
* chore(deps): upgrade to Expo SDK 56 (React Native 0.85) by @gmaclennan
in #87
* chore(ci): add release workflow by @gmaclennan in
#90
* chore: fix npm script and release build script by @gmaclennan in
#91
* chore(pack): don't try to package build files by @gmaclennan in
#92
* fix: start fastify listening by @gmaclennan in
#93
* perf(backend): switch bundler from rollup to rolldown by @gmaclennan
in #94
* fix(ci): ignore-scripts in ios npm installs by @gmaclennan in
#96
* fix(ci): replace --ignore-scripts with npm strict-allow-scripts
allowlist by @gmaclennan in
#106
* feat(config): let the consuming app supply the default project config
by @gmaclennan in
#95
* chore(release): merge prerelease branch. by @gmaclennan in
#110

## New Contributors
* @achou11 made their first contribution in
#12

**Full Changelog**:
https://github.com/digidem/comapeo-core-react-native/commits/v1.0.0-pre.2

<!--

<release-meta>{"id":342868678,"version":"v1.0.0-pre.2","npmTag":"pre","opticUrl":"https://optic-zf3votdk5a-ew.a.run.app/api/generate/"}</release-meta>
-->
@gmaclennan gmaclennan added the feature New feature (changelog) label Jun 22, 2026
gmaclennan added a commit that referenced this pull request Jun 22, 2026
## Optic Release Automation

This **draft** PR is opened by Github action
[optic-release-automation-action](https://github.com/nearform-actions/optic-release-automation-action).

A new **draft** GitHub release
[v1.0.0-pre.2](https://github.com/digidem/comapeo-core-react-native/releases/tag/untagged-352a6c41c12fd02dec37)
has been created.

Release author: @gmaclennan

#### If you want to go ahead with the release, please merge this PR.
When you merge:

- The GitHub release will be published

- The npm package with tag pre will be published according to the
publishing rules you have configured



- No major or minor tags will be updated as configured


#### If you close the PR

- The new draft release will be deleted and nothing will change

<!-- Release notes generated using configuration in .github/release.yml
at 7fe80b4 -->

## What's Changed
### 🚀 Features
* Integrate @comapeo/core via IPC over Unix sockets by @gmaclennan in
#5
* Add iOS support & test infrastructure by @gmaclennan in
#6
* iOS Phase 1: unified JS bundle + smoke test (simulator-only) by
@gmaclennan in
#15
* iOS Phase 2: xcframework Embed & Sign for native addons by @gmaclennan
in #16
* Phase 2 Android: jniLibs packaging + unified rollup loader plugin by
@gmaclennan in
#17
* android: read abiFilters from reactNativeArchitectures (#30) by
@gmaclennan in
#35
* Add rootkey persistence and lifecycle state management by @gmaclennan
in #36
* Sentry integration: Phase 1 + Phase 2a + Phase 2b by @gmaclennan in
#54
* feat(backend): polywasm-backed undici on iOS, re-enable maps plugin by
@gmaclennan in
#62
* feat(sentry): land Phase 3 — backend loader + RPC tracing by
@gmaclennan in
#63
* feat(sentry): land Phases 6 + 7a — Android exit reasons & iOS
MetricKit app-exit telemetry by @gmaclennan in
#72
* feat(sentry): migrate to @sentry/react-native v8; exit telemetry as
Application Metrics by @gmaclennan in
#73
* Map server integration by @gmaclennan in
#86
* feat(config): let the consuming app supply the default project config
by @gmaclennan in
#95
### 🐛 Bug Fixes
* fix(android): drop setUnlockedDeviceRequired from rootkey wrapper key
by @gmaclennan in
#57
* fix(backend): cache stopping/error frames for late joiners by
@gmaclennan in
#58
* fix(ios-tests): wait for STOPPING before signalling node exit by
@gmaclennan in
#59
* fix(android): drain JNI stdio pumps before returning from node::Start
by @gmaclennan in
#60
* fix(ios-tests): serialise STOPPING/STOPPED observers in
testFullLifecycleStateTransitions by @gmaclennan in
#71
* fix(sentry): make exit telemetry lossless and stop cross-process
clobbering by @gmaclennan in
#84
* fix: start fastify listening by @gmaclennan in
#93
* fix(ci): ignore-scripts in ios npm installs by @gmaclennan in
#96
* fix(ci): replace --ignore-scripts with npm strict-allow-scripts
allowlist by @gmaclennan in
#106
* fix(release): stop `npm pack --dry-run` leaking dry-run into backend
install by @gmaclennan in
#129
### ⚡ Performance
* perf(backend): switch bundler from rollup to rolldown by @gmaclennan
in #94
### ⬆️ Dependencies
* update some native deps used in backend by @achou11 in
#14
* chore(deps): upgrade to Expo SDK 56 (React Native 0.85) by @gmaclennan
in #87
### 🏗️ Maintenance
* Android Testing Infrastructure & Bug Fixes by @gmaclennan in
#3
* chore: prebuild example/android; harden instrumented tests by
@gmaclennan in
#10
* chore: adjust repo setup by @achou11 in
#12
* chore: minor fixes based on expo-doctor by @achou11 in
#13
* chore: add architecture docs & plans by @gmaclennan in
#11
* chore: post-Phase-2 cleanup — comments, plan docs, agents.md by
@gmaclennan in
#33
* refactor: simplify build-backend.ts; rollup writes directly to native
asset trees by @gmaclennan in
#34
* chore: fix eslint configuration by @achou11 in
#41
* android: audit 16 KB page alignment on every shipped .so by
@gmaclennan in
#43
* chore: move example app into apps directory by @achou11 in
#18
* refactor: per-component lifecycle state with derived ComapeoState by
@gmaclennan in
#47
* android: fold waitForFile into connect retry loop by @gmaclennan in
#52
* chore: add e2e testing app by @achou11 in
#49
* ci: drop unreliable Android emulator snapshot caching by @gmaclennan
in #64
* use npm list instead of custom traversal to get native module versions
by @achou11 in
#70
* chore(e2e): add e2e tests on browserstack via Maestro by @achou11 in
#56
* chore(ci): add release workflow by @gmaclennan in
#90
* chore: fix npm script and release build script by @gmaclennan in
#91
* chore(pack): don't try to package build files by @gmaclennan in
#92
* chore(release): merge prerelease branch. by @gmaclennan in
#110
* ci(e2e): retry BrowserStack builds on infra-class flakes by
@gmaclennan in
#113
### Other Changes
* ci: derive changelog labels from PR titles + add Dependabot by
@gmaclennan in
#114

## New Contributors
* @achou11 made their first contribution in
#12
* @optic-release-automation[bot] made their first contribution in
#112

**Full Changelog**:
https://github.com/digidem/comapeo-core-react-native/commits/v1.0.0-pre.2

<!--

<release-meta>{"id":342970724,"version":"v1.0.0-pre.2","npmTag":"pre","opticUrl":"https://optic-zf3votdk5a-ew.a.run.app/api/generate/"}</release-meta>
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature (changelog)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant