chore: INFRA-295 correct Sentry release-health session option + crash attribution - #218
Merged
MP2EZ merged 1 commit intoJul 25, 2026
Merged
Conversation
β¦ attribution
Re-scoped after a planning panel falsified the story's premise. The item assumed
release-health sessions were deliberately disabled and needed enabling. They were
not disabled.
1. The option key was dead. `ExternalErrorReporter` passed `autoSessionTracking:
false`, but @sentry/react-native reads `enableAutoSessionTracking`. The key we
set does not exist in the SDK, is forwarded verbatim to the native layer, and
is ignored there β and both native SDKs default session tracking ON. So the
"privacy-first, error-only" posture the code comment claimed was never in
effect: sessions have most likely been transmitting from every non-__DEV__
build, including the 2026-06-15 TestFlight build. Corrected to the real option
and kept ON intentionally. TypeScript cannot catch this class of bug β the RN
options type carries an index signature.
2. Crash attribution was broken. `applyAllowlist` rebuilt exception.values as
{type, value, stacktrace}, dropping `mechanism`; `level` was absent from
ALLOWED_ERROR_FIELDS. The SDK's isHardCrash() requires
mechanism.handled === false && mechanism.type === 'onerror', and beforeSend
runs BEFORE envelope creation β so no JS fatal ever marked its session
crashed. A crash-free-session-rate alert would have counted native crashes
only and read systematically optimistic, under-firing exactly when it matters.
Now preserved as explicitly type-guarded named scalars β never a spread, so
the SDK's open `mechanism.data` bag cannot ride along β plus an enum guard on
`level` so it can never become a free-text channel.
3. Contract test imports the SDK's REAL isHardCrash, so an upstream change to the
crash-attribution rule fails the build rather than silently zeroing the metric.
Runs in precommit via test:privacy.
4. Docs reconciled to reality: privacy-policy Β§5.1 disclosed session diagnostics
and the per-install identifier (it previously promised "no device identifier",
which the session `did` contradicts); DPIA v1.7 records the retrospective
finding (not a breach β no wellness content, and Sentry was already an
authorized sub-processor); runbook Β§1 corrected β its "no data source today"
warning was wrong, and the interim Number-of-Errors alert it said would be
superseded was never created (being-prod has zero metric alerts).
Session envelopes bypass beforeSend/normalizeDepth entirely (envelope session
items, not events), so the controls the original AC named never applied to them;
the review was done against the fixed session schema instead.
Compliance agent verdict: privacy-neutral β mechanism.type/handled/synthetic and
level are all closed-vocabulary SDK-controlled values, and the app never
constructs a mechanism anywhere.
NOT claimed by this change (require the Sentry dashboard + a new TestFlight
build; the Sentry MCP is read-only for alert rules): creating the crash-free-
session-rate metric alert, and verifying Releases -> Health. Recorded as an
operator checklist on the Notion item.
Verification: npm run precommit exit 0 β safety 108, clinical 106, unit 568,
privacy 51 tests passed.
π€ Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Closes INFRA-295
Re-scoped: a planning panel falsified the story's premise. The item assumed release-health session tracking was deliberately disabled and needed enabling. It was not disabled.
1. The option key was dead
ExternalErrorReporterpassedautoSessionTracking: false, but@sentry/react-nativereadsenableAutoSessionTracking. The key we set does not exist in the SDK, is forwarded verbatim to the native layer, and is ignored there β and both native SDKs default session tracking ON.So the "privacy-first, error-only" posture the code comment and runbook Β§1 both credited was never in effect: sessions have most likely been transmitting from every non-
__DEV__build, including the 2026-06-15 TestFlight build. TypeScript can't catch this β the RN options type carries an index signature.2. Crash attribution was broken (independent defect)
applyAllowlistrebuiltexception.valuesas{type, value, stacktrace}, droppingmechanism;levelwas absent fromALLOWED_ERROR_FIELDS. The SDK'sisHardCrash()requiresmechanism.handled === false && mechanism.type === 'onerror', andbeforeSendruns before envelope creation β so no JS fatal error ever marked its session crashed.A crash-free-session-rate alert built on that would have counted native crashes only and read systematically optimistic, under-firing exactly when it mattered. Now preserved as explicitly type-guarded named scalars β never a spread, so the SDK's open
mechanism.databag can't ride along β plus an enum guard onlevelso it can't become a free-text channel.3. Contract test
app/__tests__/privacy/releaseHealthSession.contract.test.ts(15 tests, runs inprecommitviatest:privacy). Red phase confirmed first:isHardCrash(scrubbed)returned false against the old code. The test imports the SDK's realisHardCrash, so an upstream change to the crash-attribution rule fails the build rather than silently zeroing the metric.4. Docs reconciled to reality
privacy-policy.mdΒ§5.1 β previously promised Sentry receives "no device identifier", contradicted by the sessiondid(a per-install UUID; noSentry.setUserexists inapp/src, so the iOS layer injectsuser.id = installationID). Replaced with an accurate disclosure.dpia-sensitive-wellness-data.mdv1.7 β retrospective control-status correction. Assessed not a breach: no wellness content exposed, and the value is a device-scoped install identifier, not PHR identifiable health information under 16 CFR Part 318. Not a Β§1 material-change trigger β Sentry was already an authorized sub-processor.post-launch-monitoring-runbook.mdΒ§1 β its "no data source today" warning was wrong, and the interim Number-of-Errors alert it said would be superseded was never created (verified live:being-prodhas one rule, the default onboarding issue alert, and zero metric alerts).Session envelopes bypass
beforeSend/normalizeDepthentirely (envelope session items, not events), so the controls the original AC named never applied to them; the review was done against the fixed session schema instead.Compliance agent verdict: privacy-neutral β
mechanism.type/handled/syntheticandlevelare all closed-vocabulary SDK-controlled values, and the app never constructs amechanismanywhere.Not claimed by this PR
Creating the crash-free-session-rate metric alert and verifying Releases β Health require the Sentry dashboard and a new TestFlight build (the Sentry MCP is read-only for alert rules). Recorded as an operator checklist on the Notion item.
Verification
npm run precommitexit 0 β safety 108, clinical 106, unit 568, privacy 51 tests passed.π€ Generated with Claude Code