Skip to content

[pull] master from getsentry:master#2012

Merged
pull[bot] merged 42 commits into
KingDEV95:masterfrom
getsentry:master
May 29, 2026
Merged

[pull] master from getsentry:master#2012
pull[bot] merged 42 commits into
KingDEV95:masterfrom
getsentry:master

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented May 29, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

JonasBa and others added 30 commits May 29, 2026 09:07
Add synonyms and alternative search terms to command palette actions
based on
analysis of top no-result queries from Amplitude data.

Many users search for features using terms that don't match any existing
action
label, details, or keyword — resulting in zero results. Since the
command palette
uses fzf subsequence matching, the query must appear as a subsequence
within a
single candidate string (label, details, or keyword). Longer queries
can't match
shorter candidates, and space-separated terms won't match their
concatenated forms.

Each addition was verified against the fzf matching algorithm to confirm
it's not
already reachable via existing candidates:

| Action | Keywords Added | Top Queries Covered |
|--------|---------------|---------------------|
| Create Alert | `alert rules`, `issue alert` | "alert rules", "issue
alert" (~15) |
| Monitors > Alerts | `alert rules`, `issue alert` | same, for
workflow-engine-ui users |
| DSN | `sentry dsn` | "Sentry DSN", "sentry d" (~8) |
| Traces | `spans`, `trace explorer` | "spans", "Trace Explorer" (~5) |
| Replays | `rum`, `session replay` | "RUM", "Session Replay" (~6) |
| Releases | `release health` | "Release Health" (2) |
| Profiles | `profiling` | "Profiling" (3) |
| Organization Tokens | `user auth tokens` | "user auth", "User Auth
Tokens" (~26) |
| Personal Tokens | `user auth tokens` | same |
| Close Account | `delete account` | "delete account", "delete" (~5) |
| Integrations | `linear` | "linear", "Linear" (~5) |
| Issue Grouping | `fingerprinting`, `fingerprint rules` | "fingerprint"
(~14) |
| Client Keys (DSN) | `allowed domains` | "allow", "allowed dom" (~14) |
| Ownership Rules | `code owners` | "code owner" (~5) |

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ithm (#116453)

No change in behavior, just doing some minor cleanup in the logic here.
…tions as issue details activities (#116425)

https://linear.app/getsentry/project/add-seer-actions-to-issue-activityaction-log-0e641e1f5dac/overview

Follow-up to #116424.

Renames the `seer-activity-timeline` feature flag to
`display-seer-actions-as-issue-activities` to clarify that this flag
only gates the *display* of Seer activities in the issue details
timeline. The *recording* of these activities is controlled by the
`issues.record-seer-actions-as-activities` option added in the above PR.
When `AutoSaveForm` gets back an error response that doesn't contain
field-level errors (e.g. `{detail: "Cannot override environment variable
EXAMPLE"}` instead of `{project_mappings: ["error"]}`), it currently
falls back to a generic "Failed to save" message. This PR attempts to
extract any `detail` errors before falling back.
`unsign` deserializes an arbitrary signed JSON payload, so narrowing its
return to `dict[str, Any]` forced callers that know the concrete shape
to cast the result. This types the return as `Any` (like `json.loads`)
so callers can annotate the deserialized shape directly — e.g. a DRF
serializer's `validate()` returning a `TypedDict` — without a cast.
…#116474)

The `OrganizationSupergroupsByGroupEndpoint` was using inline
`Group.objects.filter` calls to validate and filter group IDs. This
refactors it to use the existing `get_group_list` helper from
`sentry.api.helpers.group_index`, which is the canonical pattern for
this kind of scoped group lookup (see
`api/helpers/group_index/update.py` line 275 for the existing usage).

`get_group_list` previously lived in `group_index/update.py` — a large
mutation-focused module with heavy dependencies. Since the function is a
pure read with minimal dependencies (`Group`, `Project`, `Sequence`), it
has been extracted into a new `group_index/lookup.py`. This avoids a
circular-import risk (`update.py` ← `index.py` ← `__init__.py` ←
`update.py`) and gives the function a proper home as a first-class
export of the package via `__init__.__all__`.

`update.py` now imports `get_group_list` from `.lookup`; no behaviour
change for existing callers.

Test coverage for the endpoint has been expanded and `@cell_silo_test`
has been added to the test class to match the endpoint's
`@cell_silo_endpoint` decorator.

Refs AIML-2879
…6469)

Night-shift was the only autofix invoker that forwarded an explicit
`intelligence_level` and `reasoning_effort` (both `"high"`) to
`trigger_autofix_agent`. Every other invoker — the group autofix
endpoint, the issue-summary auto-trigger, the on-completion pipeline,
and Slack — relies on the defaults: `intelligence_level="medium"` and
the per-step `reasoning_effort` from the step config.

This drops the explicit arguments from the night-shift autofix call so
its runs use the same model as all other invokers.

The night-shift triage step still honors the configured
`intelligence_level` and `reasoning_effort` options; only the autofix
invocation changes.



Agent transcript:
https://claudescope.sentry.dev/share/p8XAfGYOrDo-lObjRdup6LquAGyi4CNn3M0hGLD_HDo
…projects/ (#116388)

<!-- Describe your PR here. -->

Backend PR #116333 promoted `POST
/organizations/{org}/experimental/projects/` to `POST
/organizations/{org}/projects/` (PUBLIC). That PR keeps the old
`/experimental/` path alive as a backward-compat alias so the frontend
doesn't break between deploys.

This PR drops the `/experimental/` path from the two hooks that call it
and updates the test mocks and generated URL registry to match.

**Changed hooks:**
- `useCreateProject` — used by the main onboarding flow and the
create-project settings page
- `useCreateProjectFromWizard` — used by the setup wizard

**Also adds** a unit test for `useCreateProject` that explicitly asserts
the URL routing logic (no team slug → org endpoint, team slug present →
teams endpoint). That coverage didn't exist before.

Depends on #116333.

### Legal Boilerplate

Look, I get it. The entity doing business as "Sentry" was incorporated
in the State of Delaware in 2015 as Functional Software, Inc. and is
gonna need some rights from me in order to utilize my contributions in
this here PR. So here's the deal: I retain all rights, title and
interest in and to my contributions, and by keeping this boilerplate
intact I confirm that Sentry can use, modify, copy, and redistribute my
contributions, under Sentry's choice of terms.
…6490)

[VDY-101: Microsoft Teams: API-driven integration
setup](https://linear.app/getsentry/issue/VDY-101/microsoft-teams-api-driven-integration-setup)

Adds the API-mode pipeline machinery for Microsoft Teams alongside the
existing server-rendered configure flow, without changing the entry
point yet. This is the first of three deploy-safe steps: the legacy
`MsTeamsExtensionConfigurationView` and the
`/extensions/msteams/configure/` URL are left untouched, so nothing
changes for users until the frontend can drive the modal and the
configure URL is later swapped to a redirect.

- `MsTeamsInitialDataSerializer` unsigns the bot's `signed_params` blob
and binds each field to top-level pipeline state.
- `MsTeamsApiStep` has no interactive UI; it signals the frontend to
auto-advance, which runs `build_integration` on the bound state.
- `build_integration` now reads top-level state, falling back to the
nested `state["msteams"]` the legacy view binds, so both flows work
during the transition.

Also adds a `can_add_externally` marker to `IntegrationProvider` for
integrations whose install is initiated from the third party's app
directory or marketplace and completed through the pipeline modal. MS
Teams sets `can_add = False` to hide the in-app install button, so the
pipeline endpoint needs this opt-in to allow the externally-initiated
install. The other already-external providers (Discord, GitHub, and
GitHub Enterprise via subclassing) are marked for consistency; it's a
no-op for them since they're `can_add = True`.

Note: mypy will be red until #116486 (typing `utils.signing.unsign` as
`Any`) merges, since the serializer's `validate()` returns a `TypedDict`
from `unsign()`. The frontend follow-up is #116488.
Seems to be an issue with nuqs on the initial tab change doing a fairly
hefty page reload. To resolve this issue, we need to make the change
"shallow".

Closes JAVASCRIPT-3A1R

---------

Co-authored-by: Codex <noreply@openai.com>
…nd auto_create_pr fields (#116352)

Consolidate `update_seer_project_settings` and
`bulk_update_seer_project_settings` into a single function
that bulk deletes/creates project options across 1+ project ids. Also,
strip seat-based business logic (stopping point → auto_create_pr
syncing, tuning validation) out of the update helper and add tuning and
auto_create_pr as update fields, so it becomes a pure write layer and
can be used by other callsites.

Followups: #116356,
#115962

#### Why:
By moving business logic out, other callsites can do updates without
hidden side effects.
- Independent auto_create_pr and tuning updates are needed by legacy
seer and will be used when we add legacy seer to the project settings
endpoints: #115962
- #116356 reroutes existing
callsites that modify seer project settings
(`_write_preferences_to_sentry_db`, `configure_seer_for_existing_org`,
`set_default_project_seer_preferences`) to the update helper to ensure
that handoff options are either cleared or added atomically AND to skip
unnecessary connected repo operations. It also removes `Project` row
locks and simplifies `clear_preference_automation_handoff`.

#### Other cleanups:
- `SeerProjectSettingsUpdate` uses snake_case keys via
`CamelSnakeSerializer`.
- Add tuning to the project settings endpoints serializers (as opposed
to consolidating tuning="off" into stopping_point="off"), again to make
it easier to support legacy seer.
- Endpoint returns `autoCreatePr` and `automationTuning` in response.
- Stopping point syncs `auto_create_pr` automatically.
#116342)

## Summary
Move the loading indicator from dropdown trigger to the dropdown list.
This is exactly how the other search bars handle it (for example
searching in the explore search bar). This resolves two issues:
1. Having the spinner in the trigger causes the trigger to change widths
when it goes from not-loading -> loading, this results in some annoying
shifting (See [DAIN-1636](https://linear.app/getsentry/issue/DAIN-1636)
for video)
2. Having the trigger on the dropdown list more accurately reflects the
state, the list of options are loading. Before it looked like the
selected item is being changed.

### Before
<img width="442" height="185" alt="image"
src="https://github.com/user-attachments/assets/e9fbe4c6-cdcc-405e-b114-47d03f24c6cf"
/>

### After
<img width="449" height="591" alt="image"
src="https://github.com/user-attachments/assets/0533bdb9-7239-4fb1-9a64-4eee0b497219"
/>


Refs DAIN-1636

---------

Co-authored-by: Claude <noreply@anthropic.com>
…xed (#116476)

Make Night Shift more conservative about which issues it autofixes. The
triage agent assigns each candidate issue one of three verdicts:

- **`autofix`** → runs a full Seer autofix to the project's stopping
point (can open a code-change PR, with no human consulted before the fix
is written)
- **`root_cause_only`** → root-cause analysis only; a human decides what
to do
- **`skip`** → nothing

This PR tightens the `autofix` decision so we only ship code changes for
issues we're convinced can be fixed correctly without human involvement.
Anything less now falls back to `root_cause_only` — investigation is
still valuable, but a human stays in the loop.

## Changes

**Stricter triage prompt** (`agentic_triage.py`)
- Frames the verdicts as a "ladder of increasing caution" — default to
the least aggressive verdict; when torn, pick the more conservative one.
- Gates `autofix` behind a hard ALL-of checklist: exact root cause
confirmed by reading code, exactly one clearly-correct fix,
small/localized change, can't change intended behavior or need human
judgment, not in a high-blast-radius area (auth, billing, security,
concurrency, migrations…), and shippable without a human reviewing the
approach.
- Makes `root_cause_only` the explicit default for anything fixable that
doesn't clear the bar.
- Notes that a high fixability score alone is **not** a reason to
autofix.

Agent transcript:
https://claudescope.sentry.dev/share/yiuFYRbvXKCU5YRlGYCutt_oRvbpmMBZoVfko81-1Xo
Now that the API-driven pipeline is in place, drop the legacy
`InstallationConfigView`, `OAuthLoginView`, and `OAuthCallbackView`
classes (and the `InstallationForm` they used), the associated Django
template, and the legacy-flow tests. `get_pipeline_views()` now returns
an empty list; `get_pipeline_api_steps()` is the only setup path.

Fixes
[VDY-103](https://linear.app/getsentry/issue/VDY-103/remove-legacy-bitbucket-server-integration-setup-views)
## Summary
- Skip 3 tests in `test_project_trace_item_details.py` that are broken
as of 05-29-2026
-
`test_convert_rpc_attribute_to_json_serializes_known_string_array_without_array_flag`
- `test_convert_rpc_attribute_to_json_exposes_array_with_array_flag`
-
`TestReplacementAttributeFiltering::test_replacement_array_shown_when_no_deprecated_source`

## Test plan
- CI should pass with these tests skipped


Will continue root causing and follow up with the relevant parties to
ensure the real cause is addressed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nter (#116496)

Extend the structural linter at
`src/sentry/apidocs/_check_response_annotation_matches_schema.py` to
support union return annotations and compare against decorator response
entries at every status code (not just 2xx).

This unblocks endpoints that want to type both their success and error
response bodies — `-> Response[FooResponse] | Response[ErrorBody]` with
corresponding decorator entries — and removes the artificial 2xx
restriction from before.

## What changed

| Aspect | Before | After |
|---|---|---|
| Annotation forms | `Response[T]` only | `Response[T]` and
`Response[T_a] \| Response[T_b] \| ...` |
| Decorator status range | 2xx entries only | All status codes |
| Decorator shape recognized |
`inline_sentry_response_serializer("Name", T)` | unchanged |
| Comparison | T-name equality (single) | Set equality on raw T names |
| Name handling | Verbatim | Verbatim (no normalization, no convention
pairing) |

## What is intentionally still skipped (no diagnostic, no false
positive)

- Plain `-> Response` annotations (the unmigrated state).
- Methods with no `@extend_schema` decorator.
- Decorator entries that aren't
`inline_sentry_response_serializer(...)`. This includes:
- Direct serializer-class references like `MonitorSerializer` — these
carry a typed output by sentry convention but no statically-resolvable
link to a `TypedDict`. The right path for these is either migrating the
entry to `inline_sentry_response_serializer(...)`, or waiting for a
generic-`Serializer[T]` refactor where the linter can read the parameter
directly from the class.
  - `OpenApiResponse(...)` wrappers.
  - `RESPONSE_*` canned constants (error documentation, no body type).
  - `None`, raw `dict`, etc.
- Union arms that aren't `Response[T]` (e.g. `Response[T] |
HttpResponse`).

In all those cases the linter exits clean — it neither false-positives
nor gives a misleading guess.

## Status-code-to-T linkage is intentionally not enforced

mypy can't model the link (the status code is runtime data), and broad
`except APIException` patterns would lose it anyway. Endpoints needing
strict status↔T guarantees should `raise <APIException>(...)` for error
paths so the success-path return is uniquely typed.

## Worked examples

**Single T (unchanged from prior behavior):**
```python
@extend_schema(responses={200: inline_sentry_response_serializer("Foo", FooResponse)})
def get(...) -> Response[FooResponse]:
    ...
# pass: decorator set {FooResponse} == annotation set {FooResponse}
```

**Union with success + typed error:**
```python
@extend_schema(
    responses={
        200: inline_sentry_response_serializer("Foo", FooResponse),
        400: inline_sentry_response_serializer("Err", ErrorBody),
    },
)
def get(...) -> Response[FooResponse] | Response[ErrorBody]:
    ...
# pass: sets {FooResponse, ErrorBody} match
```

**Drift — annotation missing a decorator-declared T:**
```python
@extend_schema(
    responses={
        200: inline_sentry_response_serializer("Foo", FooResponse),
        400: inline_sentry_response_serializer("Err", ErrorBody),
    },
)
def get(...) -> Response[FooResponse]:
    ...
# fail: decorator {FooResponse, ErrorBody} != annotation {FooResponse}
```

## Tests

16 cases in
`tests/sentry/apidocs/test_check_response_annotation_matches_schema.py`:

- Single-T match / mismatch
- Union annotation matching multi-status decorator (both arms typed)
- Annotation missing a decorator-declared T (set inequality)
- Annotation containing extra T not in decorator (set inequality)
- Multiple 2xx schemas (200 + 201) matched by union
- Unmigrated `-> Response` skipped
- Method without `@extend_schema` skipped
- `RESPONSE_*` canned constant skipped
- Direct serializer-class reference skipped (waiting on generic
Serializer[T])
- `OpenApiResponse(...)` wrapper skipped
- Union arm that isn't `Response[T]` skipped
- Async methods
- Dotted annotation form (`rest_framework.response.Response[T]`)
- `main()` exit codes (zero on clean, nonzero on mismatch)

Full src/sentry run: clean. The 43 endpoints already opted into
`Response[T]` (#116335, #116433) continue to pass.

## Follow-up paths (out of scope for this PR)

A survey of all 862 `responses={N: ...}` entries in `src/sentry` showed
that ~97 use direct serializer-class references (about as common as
`inline_sentry_response_serializer`). The linter doesn't help these
endpoints today. Two ways to bring them into coverage later:
- **Migrate** each to `inline_sentry_response_serializer("Name",
FooResponse)` with an explicit paired TypedDict.
- **Make sentry's `Serializer` generic** in its output type
(`Serializer[T]`), so the linter can extract T directly from the class's
bases. This eliminates the need for any naming convention.

Either path gives the linter a real handle. This PR doesn't attempt to
bridge it with name heuristics — those were considered and rejected as
brittle.

Co-authored-by: Claude <noreply@anthropic.com>
Remove codecov-integration feature from the Settings page
…rd-migration` (#116450)

Remove `organizations:insights-ai-and-mcp-dashboard-migration`. This
flag has 0 usages across code, frontend, and tests (dead for 78 days,
first seen dead 2026-03-12). Not present in sentry-options-automator.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01RNki2RgdHF3ZuroLVpupeQ)_

Co-authored-by: Claude <noreply@anthropic.com>
)

Currently v1 (transaction-based) spans send user attributes in
`sentry.user.*` attributes, while v2 (streaming) spans send user
attributes in conventions-defined `user.*` attributes. Only the v1
attributes are currently propagated, meaning that child spans from span
streaming SDKs are missing user info.

We can use our conventions' deprecation mechanism
(getsentry/sentry-conventions#406) to make
queries backwards compatible, but that doesn't affect segment enrichment
which reads data directly from spans.

For now, propagate both the old v1 user attributes and the new v2 user
attributes to children. Once that's done, EAP's attribute coalescing
(via above deprecation PR) will take care of the rest.

Next steps will be to clean up our use of the v1 `sentry.user.*`
attributes in Relay and Sentry, at which point we can stop propagating
them. (That work is tracked in BROWSE-535).

Fixes BROWSE-532.

---

🤖: Claude Code (Opus 4.6) used to generate the tests. I made the code
changes and reviewed the tests. Words my own.
…ndpoint (#115962)

Depends on #116352 and
#116356.

Add legacy usage-based Seer support to the project settings endpoints by
branching on `is_seer_seat_based_tier_enabled`.

**Serializer split:**
- Extract `_BaseProjectSettingsUpdateSerializer` with shared validation
(agent, integration_id, cross-field checks)
- `ProjectSettingsUpdateSerializer` (seat-based): restricted tuning
choices (off/medium), stopping_point validation via
`get_valid_automated_run_stopping_points`, auto_create_pr synced from
stopping_point
- `LegacyProjectSettingsUpdateSerializer` (usage-based): accepts all
tuning values, direct `auto_create_pr` field, no stopping_point
restrictions

### Example payloads

**Set tuning to a granular value:**
```json
PUT /api/0/projects/{org}/{project}/seer/settings/
{"automationTuning": "high"}
```

**Set stopping point (frontend sends agent=seer to clear any external
handoff):**
```json
PUT /api/0/projects/{org}/{project}/seer/settings/
{"stoppingPoint": "code_changes", "agent": "seer"}
```
This clears handoff keys (target, point, integration_id) but preserves
`auto_create_pr` so switching back to an external agent restores the
previous toggle value.

**Switch to external agent with auto_create_pr:**
```json
PUT /api/0/projects/{org}/{project}/seer/settings/
{"agent": "cursor_background_agent", "integrationId": 1, "autoCreatePr": true}
```

**Toggle auto_create_pr on an existing external agent:**
```json
PUT /api/0/projects/{org}/{project}/seer/settings/
{"autoCreatePr": false}
```

**Turn off automation:**
```json
PUT /api/0/projects/{org}/{project}/seer/settings/
{"automationTuning": "off"}
```
GET response will show `stoppingPoint: "off"`, but the stored stopping
point is unchanged — re-enabling restores it.
this removes 93 unused options identified via tracking read options via
logging in #115610 (which this PR also reverts as I'm done collecting
data) cross referenced with static analysis in
getsentry/sentry-options#123

the details are in getsentry/sentry-options#123
- `scripts/migration/safe.txt` is essentially the set intersection of
statically unreferenced options and unseen/unread options over 120 hours
of GCP production logs.

the unseen list was determined by subtracting options.seen unique events
from all known options (options.all()) and I observed no newly seen
options beyond the ~100 hour mark
(`scripts/migration/collect-seen-options-out.txt`).

getsentry options that were unused are removed here too:
getsentry/getsentry#20449
Discover was still routing issue event id clicks through the
project-event redirect, even when the row already had `issue.id`. That
made some issue-event urls messy and left the redirect to clean things
up later.

This requests `issue.id` with the hidden link context and builds the
issue event url directly. Keeps the old project redirect fallback for
rows that do not have issue context.
…116427)

Adds an optional `all_image_file_names_as_regex` field to the snapshot
upload endpoint. It works like the existing `all_image_file_names`,
except each entry is a regex pattern (matched with `fullmatch`) rather
than a literal file name. This lets callers declare the head build's
complete image set with a handful of patterns instead of an exhaustive
list — useful for selective uploads, where only changed images are sent
but the full set is needed downstream to tell `removed` images apart
from `skipped` ones.

The two fields are mutually exclusive and both require `selective`.
Every uploaded image name must be covered by the declared set (appear in
the list, or match at least one pattern), preserving the existing
invariant. `fullmatch` (not `search`) is used because the literal field
uses exact full-string equality, and full-match is its faithful regex
analog.

Renames still work: rename detection runs after the removed/skipped
partitioning by matching content hashes across
`added`/`removed`/`skipped`, and that shared logic is unchanged.

### New dependency: `google-re2`

Patterns are **client-supplied**, so they cannot be matched with a
backtracking engine (stdlib `re` or the `regex` lib) without exposing a
ReDoS vector — a pathological pattern can hang a worker. This PR matches
them with **RE2** ([`google-re2`](https://github.com/google/re2)), a
linear-time, finite-automaton engine:

- **Catastrophic backtracking is impossible by construction** — no time
budget or timeout needed.
- **Unsupported constructs (backreferences, lookaround) are rejected at
compile time** (`re2.error`), so the engine enforces the safe subset.
These make no sense for filename patterns anyway.

Compiling a pattern *is* the validation (invalid/unsupported → 400), and
the same compiled object does the matching. Pattern length (500 chars)
and count (100) caps remain as sanity bounds. Reviewed with security;
RE2's linear-time guarantee is the agreed approach over a
wall-clock-timeout mitigation.

`google-re2` was added to the internal PyPI mirror in
getsentry/pypi#2192 (merged). It ships prebuilt wheels for all our
targets and has no new transitive dependencies.

### Refactor included

To avoid a third parallel code path, the literal and regex modes are
unified behind a single `SnapshotManifest.head_image_name_matcher()`
consumed by `categorize_image_diff`, and the endpoint's coverage checks
are consolidated into one helper (`make_image_name_matcher`).
Behavior-preserving. A `root_validator` on `SnapshotManifest` enforces
the mutual-exclusion invariant at the model level.

Co-authored-by: Claude <noreply@anthropic.com>
Wire GitLab merge requests into Seer code review. This uses the old web
hook handler format rather than the new event listeners format. I
struggled too much with the listeners. I'd rather ship the old system
than deal with it. It can be ported later if/when the new system is at
parity.

# Slop

What changed:

- Processor architecture on GitlabWebhook — adds a
`WEBHOOK_EVENT_PROCESSORS` tuple + shared `_handle` loop (with
`EVENT_TYPE` enforcement) so multiple handlers can run per event. PR
persistence stays inline in `MergeEventWebhook.__call__`; only the
code-review handler runs in the (error-isolated) processor loop.
- New handle_merge_request_event handler
(seer/code_review/webhooks/merge_request.py) — runs preflight checks,
maps GitLab actions to triggers, and enqueues the review:
- open / un-drafted update → ON_READY_FOR_REVIEW; update with new
commits → ON_NEW_COMMIT; close / merge → PR-closed. Everything else
(metadata-only edits, drafts unsupported actions) is filtered.
- GitLab has no ready_for_review action, so un-drafting is detected from
the changes payload.
- De-dupes redeliveries (and the per-org dispatch fan-out) via a short
Redis TTL key.
- Routes to the provider-agnostic
/v1/scm_code_review/{review-request,pr-closed} Seer endpoints (GitLab
isn't supported by the PyGithub-based /v1/code_review/* routes).
- Provider-aware repo definitions (seer/code_review/utils.py) — split
GitHub/GitLab builders. GitLab derives owner/name from config["path"]
(not the display name) and is_private from project.visibility_level.
- Metrics generalized to accept string event names so the funnel metrics
work for merge_request.

Known limitation

Code review won't fire in production until GitLab
OrganizationContributors are seeded — the billing preflight currently
filters every MR with ORG_CONTRIBUTOR_NOT_FOUND (GitHub seeds these via
track_contributor_seat; GitLab doesn't yet). Documented in the module
docstring.
Update the dsyms endpoint to use the modern DRF-spec system.
for components that are not specifically owned by a team further down in
the codeowners, fallback to the issue-workflow team.
evanpurkhiser and others added 12 commits May 29, 2026 16:09
…ne modal (#116488)

[VDY-101: Microsoft Teams: API-driven integration
setup](https://linear.app/getsentry/issue/VDY-101/microsoft-teams-api-driven-integration-setup)

When a user installs the Sentry app from the Microsoft Teams
Marketplace, the Sentry bot posts a card linking to
`/extensions/msteams/configure/` with a signed `signed_params` blob.
That forwards to `/extensions/msteams/link/` (the org picker), and from
there the install needs to drive the API pipeline modal. This wires up
the frontend side:

- Adds `msTeamsParams` in the org-link view, returning `{signedParams}`
when `signed_params` is present in the URL query, and routes
`handleInstallClick` through it via the existing `gitHubAppListingParams
?? discordAppDirectoryParams ?? msTeamsParams` chain.
- Adds the `integrationMsTeams` pipeline definition. Its single step has
no interactive UI; all install data arrives already bound to pipeline
state, so it auto-advances when the backend returns
`appDirectoryInstall`, rendering a brief "Finishing up..." message
(guarded by a ref against React strict-mode double-fire).
- Registers the pipeline and adds `msteams` to
`UNCONDITIONAL_API_PIPELINE_PROVIDERS`.

Depends on the backend support (separate PR) and must not deploy before
it: the modal calls the API pipeline `initialize` endpoint for
`msteams`, which only works once the backend serializer / step /
`can_add_externally` changes are live. The
`/extensions/msteams/configure/` URL still routes through the legacy
server-rendered view until a follow-up swaps it to a redirect.
…#116511)

Guard against `None` entries in `exception.values` when extracting
exception
data for the eventstream/EAP pipeline.

Some events have `None` entries in their `exception.values` list. When
`_extract_exception` iterates over this list, it calls `.get("type")` on
`None`, causing `AttributeError: 'NoneType' object has no attribute
'get'`.

The fix filters `None` entries out of the exceptions list before
iteration
rather than skipping inside the loop. This keeps `exception_count`,
`stack_level` indices, and the parallel `stack_*` arrays consistent.
Also
adds `or []` to handle the case where `"values"` is explicitly `None`.

Fixes SENTRY-FOR-SENTRY-2T8
When creating autofix PRs, make sure to link the linear ticket so it'll
be closed too.
Co-Authored-By: george-sentry
<249834052+george-sentry@users.noreply.github.com>

Co-authored-by: getsentry-bot <10587625+getsentry-bot@users.noreply.github.com>
Co-authored-by: george-sentry <249834052+george-sentry@users.noreply.github.com>
…t need to update the full Seer project preference (#116356)

Followup to #116352.

Reroute existing callsites that modify seer project settings
(`_write_preferences_to_sentry_db`, `configure_seer_for_existing_org`,
`set_default_project_seer_preferences`) to the unified
`update_seer_project_settings` helper. This ensures handoff options are
cleared/added atomically and skips unnecessary connected repo
operations.

Also remove `Project` row locks from `_write_preferences_to_sentry_db`
and `clear_preference_automation_handoff` — no longer needed since
handoff options are all either cleared or added atomically, and the
other update fields may be updated independently of each other.
We had duplicate definitions of 'Author' across releases. Consolidate
them to the type `UserSerializerResponse | NonMappableUser`.

Co-authored-by: Claude <noreply@anthropic.com>
Remove `organizations:seer-issue-view`. This flag has zero usage and is
not registered in sentry-options-automator.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01KC1dc6Scf5gtgkusF5v6Ui)_
We're planning to add search filters which will allow users to query
issues by the activity types contained within them (e.g. an upcoming
`issue.agent:pr_created` filter which filters to issues with the
activity `SEER_PR_CREATED`). Those queries filter `sentry_activity` on
`project_id` plus `type`, which results in a very slow and inefficient
query without this index.

This is a huge table (>1B rows), so it is a post deployment migration.

In addition to the new index, this also removes an unused one
(`sentry_activity_weekly_report_jtcunning`) to clear some room.
…le (#116515)

Add types and example for the event attachments endpoint, in preparation
for publishing it.

Co-authored-by: Claude <noreply@anthropic.com>
The old issue details guide was still wired into anchors that no longer
exist, while the current tour had no launcher when the backend marked it
unseen.

Removes the stale guide and anchor wrappers, keeps the existing tour,
and lazy-loads the start tour modal when we should show it.

Brings back the tour, can be manually triggered `#issue-details-tour` on
the issue details page

<img width="507" height="459" alt="image"
src="https://github.com/user-attachments/assets/12a41ac3-98c8-42a0-bcf5-6349b5c22d41"
/>
@pull pull Bot locked and limited conversation to collaborators May 29, 2026
@pull pull Bot added the ⤵️ pull label May 29, 2026
@pull pull Bot merged commit 3d2d19e into KingDEV95:master May 29, 2026
@github-actions github-actions Bot added Scope: Frontend Automatically applied to PRs that change frontend components Scope: Backend Automatically applied to PRs that change backend components labels May 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

⤵️ pull Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.