Skip to content

feat(cli): deepen sentry observability -- source maps, tracing, anonymization, crash refs#628

Merged
rayhanadev merged 6 commits into
mainfrom
ray/99d8e1d2
Jun 1, 2026
Merged

feat(cli): deepen sentry observability -- source maps, tracing, anonymization, crash refs#628
rayhanadev merged 6 commits into
mainfrom
ray/99d8e1d2

Conversation

@rayhanadev
Copy link
Copy Markdown
Member

@rayhanadev rayhanadev commented Jun 1, 2026

Why

The CLI got crash reporting in #621, but the events that land in Sentry aren't very debuggable: stack frames point at the bundled dist/cli.js, there's no environment / release / trace context, the run sends PII (sendDefaultPii: true, raw cwd/argv, hostname, IP), and a user who hits a crash has no way to point us at the exact event.

This makes the telemetry actually useful for triage while keeping it anonymized and opt-out.

Before (a crash in Sentry today):

TypeError: ...
  at o (cli.js:1:48572)
  at cli.js:1:48999
# no environment/release, no project context
# carries the user's IP, hostname, and /Users/<name>/... paths

After:

TypeError: ...
  at runInspect (packages/react-doctor/src/.../inspect.ts:210:18)
# environment=production · release=react-doctor@<version>
# tags: command=inspect · origin=ci · project.framework=nextjs · project.reactMajor=18 …
# linked to the run transaction (shared trace_id); paths scrubbed to ~/… ; no IP/hostname
# and the CLI prints: "Reference (mention this when reporting): 9503e416…"

What changed

  • Source maps — emit dist/cli.js.map and upload it to Sentry with Debug IDs (scripts/sentry-sourcemaps.mjs, wired into the release + @dev publish jobs) so frames symbolicate to original TypeScript. Maps are uploaded, not shipped in the npm tarball. No-op without the SENTRY_AUTH_TOKEN / SENTRY_ORG / SENTRY_PROJECT CI secrets.
  • Tracing — each run is a Sentry transaction, and the existing Effect instrumentation (runInspect + every Effect.fn("Service.method") span) is bridged into one unified per-run trace. A user's own OTLP backend (REACT_DOCTOR_OTLP_*) still wins and is parented under the Sentry trace so the two share a trace_id.
  • Environment / run info — sets environment, a react-doctor@<version> release, configurable tracesSampleRate, and attaches the run snapshot (command, origin cli/ci/agent, package manager, Node major, …) as searchable tags + context on every event.
  • Project info — the anonymous project shape we already detect (framework, React/Expo version, TypeScript, source-file count) is attached as project.* tags + a project context block; identifying projectName/rootDirectory are omitted.
  • Anonymized by defaultsendDefaultPii: false; beforeSend/beforeSendTransaction strip hostname/server_name/device name and captured local variables, and scrub home-dir paths (username → ~) and known secrets/emails from all events (reusing core's redactSensitiveText). Drops the event if scrubbing fails.
  • Crash referencesreportErrorToSentry returns the Sentry event id; the CLI surfaces it ("Reference (mention this when reporting): …") and embeds it in the prefilled GitHub issue, and scan-time errors are linked back to the run's trace.
  • Config env vars honored at runtime: SENTRY_DSN, SENTRY_ENVIRONMENT, SENTRY_RELEASE, SENTRY_TRACES_SAMPLE_RATE (0 disables tracing), SENTRY_DEBUG.
  • Adds 53 unit tests, a changeset (minor), and README/AGENTS docs.

Opt-out and CLI-only: --no-score disables Sentry entirely, it's skipped under tests, and the programmatic @react-doctor/api library never initializes Sentry.

Eval results

RDE not run — this is an observability/infrastructure change to the CLI, not a lint rule, so there are no diagnostics to eval. Verified end-to-end instead by intercepting the real Sentry envelope from the built dist/cli.js: confirmed symbolicated/anonymized payloads (no $HOME, hostname, or server_name), project attributes on the transaction, and a printed crash reference that matches the captured event_id.

Test plan

  • pnpm --filter react-doctor test (focused Sentry suites: scrub-*, sentry-tracer, instrument, report-error, build-sentry-*, with-sentry-run-span, active-run-trace, handle-error, build-run-context, to-span-attributes — 53 passing)
  • pnpm typecheck
  • pnpm lint and pnpm format:check
  • pnpm build then a deliberate throw to eyeball a symbolicated, anonymized Sentry event (and the printed reference)

Notes for reviewers

Residual risk

  • In --json mode the crash reference isn't printed (stdout stays pure JSON); the event is still captured and findable in Sentry by trace/tags.
  • Two unrelated test failures only reproduce in this dev environment (a global ~/.config/git/ignore that lists .env.local, and CURSOR_AGENT=1); both pass in a clean env / CI.

Made with Cursor


Note

Medium Risk
Adds outbound telemetry with path/secret scrubbing and opt-out, but mis-scrubbing or scope leaks would be privacy-sensitive; release/source-map and tracer wiring also touch the published CLI bundle and CI publish path.

Overview
Expands CLI-only Sentry from basic crash dumps into release-aligned, anonymized observability: source maps (bundle sourcemap: true, scripts/sentry-sourcemaps.mjs on pnpm release and @dev publish with Debug IDs; maps not in npm), per-run transactions, and an Effect→Sentry tracer (makeSentryTracer, withSentryRunSpan, applyObservability) so existing runInspect / Effect.fn spans become one trace. User OTLP (REACT_DOCTOR_OTLP_*) still wins; when both run, Effect traces parent under the Sentry trace for a shared trace_id.

Privacy and triage: sendDefaultPii: false, scrubSentryEvent on beforeSend / beforeSendTransaction (drop on scrub failure), path/username scrubbing in buildRunContext, and anonymous project.* tags from detected ProjectInfo (no repo name/root). Run metadata is centralized in buildSentryScope for every event; scan crashes link via active-run-trace + propagation context. reportErrorToSentry returns an event id surfaced in stderr and the prefilled GitHub issue; flushSentry runs on the success exit path. Still gated by --no-score / tests / SENTRY_TRACES_SAMPLE_RATE=0; programmatic API does not init Sentry.

Reviewed by Cursor Bugbot for commit 3900472. Bugbot is set up for automated code reviews on this repo. Configure here.

rayhanadev and others added 2 commits June 1, 2026 02:38
…zation, crash refs)

Builds on the crash reporting from #621:

- Source maps: emit dist/cli.js.map and upload to Sentry with Debug IDs
  (scripts/sentry-sourcemaps.mjs, wired into the release + @dev publish jobs)
  for de-minified stack traces. Maps are uploaded, not shipped in the tarball.
- Tracing: each run is a Sentry transaction; the existing Effect spans
  (runInspect + every Effect.fn service span) are bridged into one unified
  per-run trace. A user's own OTLP backend still wins and shares the trace_id.
- Environment/run info: environment + react-doctor@<version> release,
  configurable tracesSampleRate, and the run + detected project shape attached
  as searchable tags/context on every event.
- Anonymized telemetry: sendDefaultPii off; hostname/IP/device name and
  captured local variables stripped; home-dir paths and known secrets scrubbed
  from all events; identifying projectName/rootDirectory omitted.
- Crash references: the Sentry event id is surfaced in the CLI error output and
  the prefilled GitHub issue, and scan errors are linked back to the run trace.

Opt-out and CLI-only: --no-score disables Sentry entirely, it is skipped under
tests, and the programmatic @react-doctor/api library never initializes Sentry.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	packages/react-doctor/src/cli/index.ts
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 1, 2026

Open in StackBlitz

npm i https://pkg.pr.new/eslint-plugin-react-doctor@628
npm i https://pkg.pr.new/oxlint-plugin-react-doctor@628
npm i https://pkg.pr.new/react-doctor@628

commit: 3900472

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

React Doctor

React Doctor found 38 files changed in this pull request, but none matched the files covered by its enabled checks.

Scope: 38 files changed on ray/99d8e1d2 vs. main.

View workflow run

Generated by React Doctor. Questions? Contact founders@million.dev.

Comment thread packages/react-doctor/src/cli/utils/with-sentry-run-span.ts
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 7 additional findings.

Open in Devin Review

Bugbot flagged that the module-level project-info and active-run-trace refs
weren't reset per run, so a multi-project workspace scan could attach a prior
project's tags (or a stale trace) to a later project's error thrown before its
beforeLint hook. Clear both at the start of each inspect() via
resetSentryRunState(); add a regression test.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 2 potential issues.

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 8d3e9ba. Configure here.

Comment thread packages/react-doctor/src/cli/utils/with-sentry-run-span.ts Outdated
Comment thread packages/react-doctor/src/cli/utils/build-sentry-scope.ts
rayhanadev and others added 3 commits June 1, 2026 02:59
Address Bugbot: clearing only the trace on success (not the project ref) left
post-scan work in inspectAction (finalize/handoff, or the next workspace
project) tagged with a stale project and unlinked. Now inspect() clears BOTH
the project ref and the run trace when a scan finishes cleanly, and persists
both on a thrown error so the command catch can still attribute and link the
crash. withSentryRunSpan only records the trace; inspect() owns the reset.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	packages/react-doctor/README.md
Co-authored-by: Cursor <cursoragent@cursor.com>
@rayhanadev rayhanadev changed the title feat(cli): deepen Sentry observability — source maps, tracing, anonymization, crash refs feat(cli): deepen sentry observability -- source maps, tracing, anonymization, crash refs Jun 1, 2026
@rayhanadev rayhanadev merged commit e9e71bb into main Jun 1, 2026
18 checks passed
@rayhanadev rayhanadev deleted the ray/99d8e1d2 branch June 1, 2026 10:19
@github-actions github-actions Bot mentioned this pull request Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant