Addresses the high- and medium-priority issues from the
review subagent's pass over Phase 1+2a+2b:
Boot transaction lifecycle (was: review #1, #15)
- applyAndEmit now closes bootTx and drains in-flight phase
spans on STOPPING / STOPPED transitions too — not just
STARTED / ERROR. stop()-from-STARTING transitions to
STOPPING (rule 3 of deriveLifecycleState) and bypassed
both terminals; destroy() forcing STOPPED-via-stopRequested
did the same. Status mapped: STARTED→ok, ERROR→
internal_error, STOPPING/STOPPED→cancelled.
- startBootTransaction now passes a TransactionContext
carrying TracesSamplingDecision(true, 1.0). The previous
TransactionOptions-only setup didn't actually force
sampling, so with the SDK default tracesSampleRate=0.0 the
boot transaction was dropped before reaching the wire.
SentryConfig misconfig handling (was: review #3)
- Both Kotlin and Swift readers used to crash on (DSN-set,
environment-missing) — meant to be "fail loud" but a stale
prebuild from before the validation was added would crash
every cold start with no recovery. Now log loud (System.err
on Android since android.util.Log isn't mocked on JVM
tests; NSLog on iOS) and return null (Sentry off). Updated
test renamed to assert "returns null, doesn't throw".
Span op/description ordering (was: review #19)
- transaction.startChild(op, description) — op is the indexed
dashboard column. Was passing ("boot", "boot.<phase>"),
swapped to ("boot.<phase>", human-readable description) so
the dashboard groups by the phase taxonomy that matches
the bench backend's boot-spans.js helper.
Plugin idempotency (was: review #7)
- Previously, dropping `props.sentry` from the plugin
registration left stale meta-data / plist entries from a
previous prebuild (with `expo prebuild --no-clean`). Plugin
now passes through a no-Sentry cleanup mod that strips
every key it owns; consumer-owned keys (e.g. io.sentry.* set
by @sentry/react-native's plugin) are untouched.
messageerror payload truncation (was: review #8)
- src/sentry.ts now truncates the wrapped error message to
256 chars before forwarding to captureException. The
control-frame parser surfaces offending input verbatim,
which can include arbitrary bytes from a corrupted frame —
truncating keeps Sentry events small and readable.
IPC + SEND_ERROR_NATIVE breadcrumbs/events (was: review #9, #10)
- NodeJSIPC's onConnectionStateChange callback wired in the
FGS-side controlIpc construction; emits comapeo.ipc
breadcrumbs at info (warning on Error). Per §7.4.5.
- SEND_ERROR_NATIVE_TIMEOUT_MS firing now captures a
level=warning event with timeout:errorNativeForward tag.
Per §7.4.4.
Logging swallowed surprises (was: review low-priority)
- SentryFgsBridge's empty `catch (t: Throwable) {}` blocks
now Log.w so debug builds notice swallowed bridge / SDK
bugs.
Post-init bridge tests (was: review #6)
- New SentryFgsBridgeImplTest spins up a real Sentry hub via
the cross-platform Sentry.init(SentryOptions) path with an
in-memory ITransport. Covers: addBreadcrumb (no envelope on
its own), captureException + captureMessage (envelope
enqueued), startBootTransaction with global
tracesSampleRate=0.0 (must still reach transport thanks
to the TracesSamplingDecision override — regression test
for the §15 bug above), boot span lifecycle, finishSpan
with cancelled status, unknown level fallback to INFO.
All Sentry-related tests pass: 25 cases across
SentryConfigTest (8), SentryFgsBridgeTest (10),
SentryFgsBridgeImplTest (7).
Verified locally:
- npm run lint clean
- npx tsc --noEmit clean
- ./gradlew :comapeo-core-react-native:testDebugUnitTest
passes
- ./gradlew :comapeo-core-react-native:compileDebugKotlin
succeeds with sentry-android on the compile classpath
iOS does not support foreground services like Android, so the Node.js
backend is shut down gracefully when the app enters background or is
terminated. Uses UIApplication.beginBackgroundTask to request additional
execution time for clean shutdown via the same length-prefixed JSON
protocol over Unix domain sockets.
Key additions:
getState, events matching Android API)
file watching, IPC socket integration tests)
https://claude.ai/code/session_0121j34VvA2xbvumSPvG3AEf