Monorepo with 40+ packages in @sentry/*, managed with Yarn workspaces and Nx.
- Volta for Node.js/Yarn/PNPM version management
- Requires
VOLTA_FEATURE_PNPM=1 - After cloning:
yarn install && yarn build - Never change Volta, Yarn, or package manager versions unless explicitly asked
Prefer LSP over Grep/Read for code navigation — it's faster, precise, and avoids reading entire files:
workspaceSymbolto find where something is definedfindReferencesto see all usages across the codebasegoToDefinition/goToImplementationto jump to sourcehoverfor type info without reading the file
Use Grep only when LSP isn't available or for text/pattern searches (comments, strings, config).
After writing or editing code, check LSP diagnostics and fix errors before proceeding.
Use yarn, never npm or pnpm. Scripts live in the root package.json.
yarn build:dev:filter @sentry/<pkg> builds one package and its deps.
Single package: cd packages/<name> && yarn test
AI commits MUST include a Co-Authored-By line with the appropriate committer email when known:
Co-Authored-By: <Claude model name> <noreply@anthropic.com>
Co-Authored-By: <OpenAI/ChatGPT model name> <codex@openai.com>
Co-Authored-By: <Cursor agent name> <cursoragent@cursor.com>
Use the Cursor email for Cursor, even when it runs a Claude or OpenAI model. Omit the line only when there is no known committer email address for the agent.
Uses Git Flow (see docs/gitflow.md).
- All PRs target
develop(NOTmaster) master= last released state — never merge directly- Feature branches:
feat/descriptive-name - Never update dependencies,
package.json, or build scripts unless explicitly asked
yarn formatyarn build:devyarn lintyarn test- NEVER push on
develop
- Do NOT add a "Test plan" / "Testing" checklist to PR bodies. CI runs the full test suite on every PR — a hand-rolled checklist duplicates that signal and rots fast. Write the summary content directly and add a Root cause section only if relevant.
- Omit the "Summary" heading in PR bodies — lead with the summary text itself, no
## Summaryheader. - Include
Fixes #<issue-number>somewhere in the PR body so the merge auto-closes the linked issue. - Always open PRs as draft.
- Include reasoning of changes in the PR description, as well as decisions that were taken during implementation. Do not explain the implementation that can be viewed in the code.
packages/types/is deprecated — never modify it. Types live inpackages/core/.- An AI provider integration spans three places: core instrumentation in
packages/core/src/tracing/{provider}/, the Node integration inpackages/node/src/integrations/tracing/{provider}/, and the edge runtime inpackages/cloudflare/src/integrations/tracing/{provider}.ts.
- This project uses Oxlint and Oxfmt — NOT ESLint or Prettier
- Never run
eslint,npx eslint, or any ESLint CLI — useyarn lint(Oxlint) instead - Never run
prettier— useyarn format(Oxfmt) instead - ESLint packages in the repo are legacy/e2e test app dependencies — ignore them
- Do not create, modify, or suggest
.eslintrc,eslint.config.*, or.prettierrcfiles
- Follow existing conventions — check neighboring files
- Reach for existing utils before writing a new one. Most shared helpers live in
@sentry/core(packages/core/src/utils/), with browser helpers inpackages/browser-utils/. Search first (LSPworkspaceSymbolor grep) for common needs (type guards inis.ts, object/array helpers,normalize,dsn,merge, string/url helpers). Reuse or extend the existing util rather than adding a near-duplicate; only introduce a new util when nothing fits. - Only use libraries already in the codebase
- Never expose secrets or keys
- When modifying files, cover all occurrences (including
src/andtest/) - Comments explain why, never what — never add a comment that restates what the code does or describes the change being made; only comment when the reasoning isn't obvious from the code itself
- Do not use
expect(someSpy.mock.calls[0]?.[0])or similar constructs to check what a spy was called with. Instead useexpect(someSpy).toHaveBeenCalledWith(...)or derivatives for a more readable and less brittle test assertion.
Do NOT "fix" a bundler, runtime, or platform incompatibility by making an import lazy or opaque — createRequire, require-inside-a-function, dynamic import(), computed specifiers. Not all bundlers understand createRequire, and anything opaque to static analysis just moves the breakage to a different consumer (pnpm isolation, workerd, Turbopack, nft tracing) while masking the real defect. SDK code must stay statically analyzable.
Before even proposing lazy loading:
- Reproduce the failure and read the actual error — not a plausible theory about it. If the error is swallowed, extract it (debug logging, running the server/bundle directly) before choosing a fix.
- Fix the root cause at the layer it lives in, in roughly this order: build output shape (rollup/commonjs options like
interop,strictRequires,requireReturnsDefault,output.paths), module resolution (exportsmaps, self-references, absolute-path externals), packaging (what ships in the tarball, bundled vs external deps), and only then consumer-side configuration. - If, after exhausting these, lazy loading still seems necessary, stop and ask — explain what was tried and why nothing else works. Do not implement it first.
Task-specific instructions live in .claude/skills/. Each skill lists its
own trigger, so consult that directory rather than this file.