Skip to content

Commit fc52bb8

Browse files
adinauerclaude
andauthored
docs: Add agent rules for API, options, and PR workflows (#5133)
* docs: Add agent rules for API, options, and PR workflows Add cursor rules for public API surface (api.mdc), SDK options (options.mdc), and PR workflow including stacked PRs (pr.mdc). Update overview_dev.mdc to reference the new rules. Add Claude Code skill for creating pull requests. Co-Authored-By: Claude <noreply@anthropic.com> * ref: Remove create-java-pr skill from PR Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8124101 commit fc52bb8

File tree

4 files changed

+458
-0
lines changed

4 files changed

+458
-0
lines changed

.cursor/rules/api.mdc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
alwaysApply: false
3+
description: Public API surface, binary compatibility, and common classes to modify
4+
---
5+
# Java SDK Public API
6+
7+
## API Compatibility
8+
9+
Public API is tracked via `.api` files generated by the [Binary Compatibility Validator](https://github.com/Kotlin/binary-compatibility-validator) Gradle plugin. Each module has its own file at `<module>/api/<module>.api`.
10+
11+
- **Never edit `.api` files manually.** Run `./gradlew apiDump` to regenerate them.
12+
- `./gradlew check` validates current code against `.api` files and fails on unintended changes.
13+
- `@ApiStatus.Internal` marks classes/methods as internal — they still appear in `.api` files but are not part of the public contract.
14+
- `@ApiStatus.Experimental` marks API that may change in future versions.
15+
16+
## Key Public API Classes
17+
18+
### Entry Point
19+
20+
`Sentry` (`sentry` module) is the static entry point. Most public API methods on `Sentry` delegate to `getCurrentScopes()`. When adding a new method to `Sentry`, it typically calls through to `IScopes`.
21+
22+
### Interfaces
23+
24+
| Interface | Description |
25+
|-----------|-------------|
26+
| `IScope` | Single scope — holds data (tags, extras, breadcrumbs, attributes, user, contexts, etc.) |
27+
| `IScopes` | Multi-scope container — manages global, isolation, and current scope; delegates capture calls to `SentryClient` |
28+
| `ISpan` | Performance span — timing, tags, data, measurements |
29+
| `ITransaction` | Top-level transaction — extends `ISpan` |
30+
31+
### Configuration
32+
33+
`SentryOptions` is the base configuration class. Platform-specific subclasses:
34+
- `SentryAndroidOptions` — Android-specific options
35+
- Integration modules may add their own (e.g. `SentrySpringProperties`)
36+
37+
New features must be **opt-in by default** — add a getter/setter pair to the appropriate options class.
38+
39+
### Internal Classes (Not Public API)
40+
41+
| Class | Description |
42+
|-------|-------------|
43+
| `SentryClient` | Sends events/envelopes to Sentry — receives captured data from `Scopes` |
44+
| `SentryEnvelope` / `SentryEnvelopeItem` | Low-level envelope serialization |
45+
| `Scope` | Concrete implementation of `IScope` |
46+
| `Scopes` | Concrete implementation of `IScopes` |
47+
48+
## Adding New Public API
49+
50+
When adding a new method that users can call (e.g. a new scope operation), these classes typically need changes:
51+
52+
### Interfaces and Static API
53+
1. `IScope` — add the method signature
54+
2. `IScopes` — add the method signature (usually delegates to a scope)
55+
3. `Sentry` — add static method that calls `getCurrentScopes()`
56+
57+
### Implementations
58+
4. `Scope` — actual implementation with data storage
59+
5. `Scopes` — delegates to the appropriate scope (global, isolation, or current based on `defaultScopeType`)
60+
6. `CombinedScopeView` — defines how the three scope types combine for reads (merge, first-wins, or specific scope)
61+
62+
### No-Op and Adapter Classes
63+
7. `NoOpScope` — no-op stub for `IScope`
64+
8. `NoOpScopes` — no-op stub for `IScopes`
65+
9. `ScopesAdapter` — delegates to `Sentry` static API
66+
10. `HubAdapter` — deprecated bridge from old `IHub` API
67+
11. `HubScopesWrapper` — wraps `IScopes` as `IHub`
68+
69+
### Serialization (if the data is sent to Sentry)
70+
12. Add serialization/deserialization in the relevant data class or create a new one implementing `JsonSerializable` and `JsonDeserializer`
71+
72+
### Tests
73+
13. Write tests for all implementations, especially `Scope`, `Scopes`, `SentryTest`, and any new data classes
74+
14. No-op classes typically don't need separate tests unless they have non-trivial logic
75+
76+
## Protocol / Data Model Classes
77+
78+
Classes in the `io.sentry.protocol` package represent the Sentry event protocol. They implement `JsonSerializable` for serialization and have a companion `Deserializer` class implementing `JsonDeserializer`. When adding new fields to protocol classes, update both serialization and deserialization.
79+
80+
## Namespaced APIs
81+
82+
Newer features are namespaced under `Sentry.<feature>()` rather than added directly to `Sentry`. Each namespaced API has an interface, implementation, and no-op. Examples:
83+
84+
- `Sentry.logger()` → `ILoggerApi` / `LoggerApi` / `NoOpLoggerApi` (structured logging, `io.sentry.logger` package)
85+
- `Sentry.metrics()` → `IMetricsApi` / `MetricsApi` / `NoOpMetricsApi` (metrics)
86+
87+
Options for namespaced features are similarly nested under `SentryOptions`, e.g. `SentryOptions.getMetrics()`, `SentryOptions.getLogs()`.
88+
89+
These APIs may share infrastructure like the type system (`SentryAttributeType.inferFrom()`) — changes to shared components (e.g. attribute types) may require updates across multiple namespaced APIs.

.cursor/rules/options.mdc

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
alwaysApply: false
3+
description: Adding and modifying SDK options
4+
---
5+
# Adding Options to the SDK
6+
7+
New features must be **opt-in by default**. Options control whether a feature is enabled and how it behaves.
8+
9+
## Namespaced Options
10+
11+
Newer features use namespaced option classes nested inside `SentryOptions`, e.g.:
12+
- `SentryOptions.getLogs()` → `SentryOptions.Logs`
13+
- `SentryOptions.getMetrics()` → `SentryOptions.Metrics`
14+
15+
Each namespaced options class is a `public static final class` inside `SentryOptions` with its own fields, getters/setters, and callbacks (e.g. `BeforeSendLogCallback`, `BeforeSendMetricCallback`).
16+
17+
A typical namespaced options class contains:
18+
- `enabled` boolean (default `false` for opt-in)
19+
- `sampleRate` double (if the feature supports sampling)
20+
- `beforeSend` callback interface (nested inside the options class)
21+
22+
To add a new namespaced options class:
23+
1. Create the `public static final class` inside `SentryOptions` with fields, getters/setters, and any callback interfaces
24+
2. Add a private field on `SentryOptions` initialized with `new SentryOptions.MyFeature()`
25+
3. Add getter/setter on `SentryOptions` annotated with `@ApiStatus.Experimental`
26+
27+
## Direct (Non-Namespaced) Options
28+
29+
Options that apply globally across the SDK (e.g. `dsn`, `environment`, `release`, `sampleRate`, `maxBreadcrumbs`) live as direct fields on `SentryOptions` with getter/setter pairs. Use this pattern for options that aren't tied to a specific feature namespace.
30+
31+
## Configuration Layers
32+
33+
Options can be set through multiple layers. When adding a new option, consider which layers apply:
34+
35+
### 1. SentryOptions (always required)
36+
37+
The core options class. Add the field (or nested class) with getter/setter here.
38+
39+
**File:** `sentry/src/main/java/io/sentry/SentryOptions.java`
40+
41+
**Tests:** `sentry/src/test/java/io/sentry/SentryOptionsTest.kt`
42+
- Test the default value
43+
- Test merge behavior (see layer 2)
44+
45+
### 2. ExternalOptions (sentry.properties / environment variables)
46+
47+
Allows setting options via `sentry.properties` file or system properties. Fields use nullable wrapper types (`@Nullable Boolean`, `@Nullable Double`) since unset means "don't override the default."
48+
49+
**File:** `sentry/src/main/java/io/sentry/ExternalOptions.java`
50+
- Add `@Nullable` fields with getter/setter for each externally configurable option (e.g. `enableMetrics`, `logsSampleRate`)
51+
- Wire them in the static `from(PropertiesProvider)` method:
52+
- Boolean: `propertiesProvider.getBooleanProperty("metrics.enabled")`
53+
- Double: `propertiesProvider.getDoubleProperty("logs.sample-rate")`
54+
55+
**File:** `sentry/src/main/java/io/sentry/SentryOptions.java` — `merge()` method
56+
- Add null-check blocks to apply each external option onto the namespaced options class:
57+
```java
58+
if (options.isEnableMetrics() != null) {
59+
getMetrics().setEnabled(options.isEnableMetrics());
60+
}
61+
if (options.getLogsSampleRate() != null) {
62+
getLogs().setSampleRate(options.getLogsSampleRate());
63+
}
64+
```
65+
66+
**Tests:**
67+
- `sentry/src/test/java/io/sentry/ExternalOptionsTest.kt` — test true/false/null for booleans, valid values and null for doubles
68+
- `sentry/src/test/java/io/sentry/SentryOptionsTest.kt` — test merge applies values and test merge preserves defaults when unset
69+
70+
### 3. Android Manifest Metadata (Android only)
71+
72+
Allows setting options via `AndroidManifest.xml` `<meta-data>` tags.
73+
74+
**File:** `sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java`
75+
- Add a `static final String` constant for the key (e.g. `"io.sentry.metrics.enabled"`)
76+
- Read it in `applyMetadata()` using `readBool(metadata, logger, CONSTANT, defaultValue)`
77+
- Apply to the namespaced options, e.g. `options.getMetrics().setEnabled(...)`
78+
79+
**Tests:** `sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt`
80+
- Test default value preserved when not in manifest
81+
- Test explicit true
82+
- Test explicit false
83+
84+
### 4. Spring Boot Properties (Spring Boot only)
85+
86+
`SentryProperties` extends `SentryOptions`, so namespaced options (nested classes) are automatically available as Spring Boot properties without extra code. For example, `SentryOptions.Logs` is automatically mapped to `sentry.logs.enabled` in `application.properties`.
87+
88+
No additional code is needed for namespaced options — Spring Boot auto-configuration handles this via property binding on the `SentryOptions` class hierarchy.
89+
90+
**Tests:** `sentry-spring-boot*/src/test/kotlin/.../SentryAutoConfigurationTest.kt`
91+
- Add the property (e.g. `"sentry.logs.enabled=true"`) to the existing `resolves all properties` test
92+
- Assert the value is set on the resolved `SentryProperties` bean
93+
- There are three Spring Boot modules with separate test files: `sentry-spring-boot`, `sentry-spring-boot-jakarta`, `sentry-spring-boot-4`
94+
95+
### 5. Reading Options at Runtime
96+
97+
Features check their options at usage time. For namespaced features the check typically happens in the feature's API class (e.g. `LoggerApi`, `MetricsApi`):
98+
- Check `options.getLogs().isEnabled()` early and return if disabled
99+
- Apply sampling via `options.getLogs().getSampleRate()` if applicable
100+
- Apply `beforeSend` callback in `SentryClient` before sending
101+
102+
When a feature has its own capture path (e.g. `captureLog`), the relevant classes are:
103+
- `ISentryClient` — add the capture method signature
104+
- `SentryClient` — implement capture, including `beforeSend` callback execution
105+
- `NoOpSentryClient` — add no-op stub
106+
107+
## Checklist for Adding a New Namespaced Option
108+
109+
1. `SentryOptions.java` — nested options class + getter/setter on `SentryOptions`
110+
2. `ExternalOptions.java` — `@Nullable` fields + wiring in `from()`
111+
3. `SentryOptions.java` `merge()` — apply external options to namespaced class
112+
4. `ManifestMetadataReader.java` — Android manifest support (if Android-relevant)
113+
5. `SentryAutoConfigurationTest.kt` — Spring Boot property binding tests (all three Spring Boot modules)
114+
6. Tests for all of the above (`SentryOptionsTest`, `ExternalOptionsTest`, `ManifestMetadataReaderTest`)
115+
7. Run `./gradlew apiDump` — the nested class and its methods appear in `sentry.api`

.cursor/rules/overview_dev.mdc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ These rules are automatically included in every conversation:
1515
Use the `fetch_rules` tool to include these rules when working on specific areas:
1616

1717
### Core SDK Functionality
18+
- **`api`**: Use when working with:
19+
- Adding or modifying public API surface
20+
- Binary compatibility, `.api` files, `apiDump`
21+
- Understanding which classes to modify for new API (interfaces, implementations, no-ops, adapters)
22+
- `IScope`, `IScopes`, `Sentry` static API
23+
- Attributes, logging API, protocol classes
24+
25+
- **`options`**: Use when working with:
26+
- Adding or modifying SDK options (`SentryOptions`, namespaced options)
27+
- External options (`ExternalOptions`, `sentry.properties`, environment variables)
28+
- Android manifest metadata (`ManifestMetadataReader`)
29+
- Spring Boot properties (`SentryProperties`)
30+
1831
- **`scopes`**: Use when working with:
1932
- Hub/Scope management, forking, or lifecycle
2033
- `Sentry.getCurrentScopes()`, `pushScope()`, `withScope()`
@@ -63,6 +76,13 @@ Use the `fetch_rules` tool to include these rules when working on specific areas
6376

6477
- **`new_module`**: Use when adding a new integration or sample module
6578

79+
### Workflow
80+
- **`pr`**: Use when working with:
81+
- Creating pull requests
82+
- Stacked PRs, PR naming, stack comments
83+
- PR changelog entries
84+
- Merging or syncing stacked branches
85+
6686
### Testing
6787
- **`e2e_tests`**: Use when working with:
6888
- System tests, sample applications
@@ -76,6 +96,8 @@ Use the `fetch_rules` tool to include these rules when working on specific areas
7696
2. **Fetch on-demand**: Use `fetch_rules ["rule_name"]` when you identify specific domain work
7797
3. **Multiple rules**: Fetch multiple rules if task spans domains (e.g., `["scopes", "opentelemetry"]` for tracing scope issues)
7898
4. **Context clues**: Look for these keywords in requests to determine relevant rules:
99+
- Public API/apiDump/.api files/binary compatibility/new method → `api`
100+
- Options/SentryOptions/ExternalOptions/ManifestMetadataReader/sentry.properties → `options`
79101
- Scope/Hub/forking → `scopes`
80102
- Duplicate/dedup → `deduplication`
81103
- OpenTelemetry/tracing/spans → `opentelemetry`
@@ -84,3 +106,4 @@ Use the `fetch_rules` tool to include these rules when working on specific areas
84106
- System test/e2e/sample → `e2e_tests`
85107
- Feature flag/addFeatureFlag/flag evaluation → `feature_flags`
86108
- Metrics/count/distribution/gauge → `metrics`
109+
- PR/pull request/stacked PR/stack → `pr`

0 commit comments

Comments
 (0)