Skip to content

feat(events): Put scope values in event data and filters - #2399

Merged
dcramer merged 2 commits into
mainfrom
feat/event-scope-filters
Oct 30, 2026
Merged

dcramer merged 2 commits into
mainfrom
feat/event-scope-filters

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Refs #2362 (item 7, slice A).

Item 7 makes an event automation trigger namespace + events + match, with scope as optional filters. This slice is the prerequisite: every event now sets its scope values in trusted data, and each value is a declared filter with a correct events list. A new test in each plugin proves that every identifier can become filters that select the same events, and never more. Core triggers, identifiers, watches, matching, and saved rows do not change. Existing watches and automations behave the same; new filters become available.

Per service

GitHub

  • repo on every event (deployment, issue, pull request, release).
  • pullRequest on pull request events. A check suite that GitHub links to no pull request (main-branch builds) has none. pullRequest stays listed for pull_request.checks.*, because suites linked to a pull request set it, and an owner/repo#n identifier must convert to repo + pullRequest for check events.
  • issue on issue events.
  • commitSha on deployment and check suite events, only for a full SHA. Check suite data used headSha, which could be short and mixed case. A check suite with a short SHA carries no commitSha. This is on purpose: a short SHA cannot match a full one exactly.
  • deploymentId on deployment events, to match Vercel.
  • environment is trimmed, then lowercased, the same as the identifier. Before, data kept spaces that the identifier dropped.
  • tag on release.published, which had no data before. See "Case decisions".
  • Deployment, release, and check suite data go through one strict Zod schema each. Ready-for-review data (isDraft: false) now goes through pullRequestEventData too. The lazy openerUsername, openerRole, and authors values go through a strict lookup schema in pull-request-match-data.ts. Check suite data drops scope: "check_suite", a constant that the event type already says. suiteConclusion stays as a detail, not a filter.
  • GITHUB_ISSUE_COMMENT_MATCH_FIELDS is now GITHUB_ISSUE_MATCH_FIELDS. release_source now has match fields.

Sentry

  • projects on issue and metric alert events, and issueId on issue events (new filter). projects is a list: true field on both. An issue has a one-item list. One list key fits both because the project resource type gets both kinds of event, and mergeEventMatchFields needs one kind.
  • org is not in data and is not a filter. Since ref(sentry): Remove SENTRY_WEBHOOK_ORG #2386, one Sentry internal integration belongs to one org, and its webhook secret scopes every delivery to that org. So org is the same on every event of an install, and a filter on it can never narrow anything. org stays in identifiers for now, because saved Watches use those strings. Changing them is a separate decision.
  • Sentry sends no org slug in issue or metric alert webhooks. The issue has only the API URL (/api/0/organizations/<org>/issues/...), and the alert has alert_rule.organization_id (a number) and web_url. Only the installation webhook has installation.organization.slug, and the plugin does not store it. So the plugin still reads the org from the URL, but only to build identifiers and to ignore payloads with no readable org (ref(sentry): Remove SENTRY_WEBHOOK_ORG #2386 behavior). That parsing is now one Zod-checked function, organizationSlug, with a comment that says why.
  • Issue and metric alert data go through Zod schemas. The issue and metric_alert resource types now declare their match fields.

Vercel: projectId in data and as a filter. deploymentId is now a filter too. Data goes through a Zod schema. All four filters now list their events, like the other plugins. I did not add team: identifiers do not include it, and project ids are unique.

Linear: issueIdentifier is now a filter (uppercase, such as SRE-123). teamKey has an events list, so the slice B namespace merge works. Issue data is built with a strict Zod schema, like the other plugins.

GitHub and Sentry now use one order: drop missing values first, then parse with the strict schema (definedValues before .parse).

Case decisions

Scope values use the same form as identifiers, so a saved identifier can convert to exact filters. GitHub and Sentry lowercase every identifier, so repo, commitSha, projects, and environment are lowercase. This also changes existing data.repo, which copied full_name unchanged. GitHub ignores case in repo names, and the plugin's DB lookups already lowercase. Vercel ids keep their case (no identifier normalization). Linear values stay uppercase.

Release tag is the trimmed tag with only ASCII letters lowercased. It is not URI-encoded, so people and agents filter on release/1.0, not release%2f1.0. The release-source identifier has the URI-encoded tag, and the plugin lowercases the whole identifier. Encoding turns every non-ASCII character into %XX, so only ASCII letters change case there. Conversion must decode the identifier's tag part (and the environment part of a deployment-source identifier). The comment on gitHubReleaseTagDataSchema says this. A non-ASCII tag stays exact: Ü and ü stay different. The raw tag also stays in the untrusted text. Trade-off: tag text from people is now in trusted data. Git tags cannot hold spaces or control characters, so that text is short.

A blank tag (only spaces) cannot happen on GitHub, but if one arrives, the release event is skipped. It no longer throws and fails the delivery.

Declaring repo makes older saved rows that already filter on repo valid again. That is expected.

Notes for slice C (conversion)

  • org/project converts to projects: [project], and org/project#issueId to projects: [project] + issueId. The org part is dropped, because the install already fixes it. metric-alert:org/id converts to alertRuleId.
  • Big Sentry metric alert rules: when the project list does not fit the 4,000-byte data limit, projects is left out. Estimated threshold: about 72 projects with 50-character slugs. A converted org/project trigger then misses those alerts. It never widens. David accepted this limit. Slice C must keep this rule: keep the identifier for that case or block the row, never widen.
  • A short-SHA check suite has no commitSha, so a commit filter does not match it.

Tests

  • New identifierMatchMismatches in @sentry/junior-testing/event-filters, next to filterEventMismatches. Each plugin passes its published events, a conversion function, and eventMatches from plugin-api. For every event, the match of its identifier must use only filters that the plugin declares for that event type, must match the event, and must not match any event of the same type that did not go to that identifier. Every event needs such an other event, so the fixtures differ in each identifier part (same parent with another leaf, and another parent).
  • One conversion test per plugin. GitHub covers a tag with / and a non-ASCII letter, two tags that differ only in a non-ASCII letter, and an environment with spaces. The new test fails without the Sentry issueId filter, without the Linear issueIdentifier filter, with URI-encoded tags, and without the environment trim. I checked each of these.
  • The GitHub fixture moved to a new tests/event-data.test.ts, shared by the filter test and the conversion test. github-plugin.test.ts is 229 lines shorter.
  • filterEventMismatches checks each value per event against the field's kind, enum, and list, beside the events list check. A blank-tag release case was added.

Ran: pnpm typecheck, pnpm lint, the GitHub (255), Sentry (22), Vercel (33), and Linear (10) suites, and the core events/automations tests. One core test, event-automations > delivers the current automation card, fails in my sandbox on main too, because the title comes back generated.

CI on da00392aa: ci / required and every other check pass. All eval suites ran on this head: guardian 94/94, integration 102/102, and behavioral 81/83, which is above its score floor (behavioral / score passes). The two behavioral misses are skills/routing.eval.ts (user-callable skill rejection) and google/scheduling.eval.ts (recurring two-person slot). This PR does not touch either area. The evals summary check is still pending. It waits to compare with an eval run on the base commit 2236380, and no eval run exists for that commit on main.

Notes and follow-ups

  • Event text now shows the new data keys. Eval recordings that include GitHub deployment, release, or check events may need to record again.
  • Slice B items that touch these fields: lazy match data for rows without an identifier (GitHub authors/opener*), and required scope filters per event (for example, repo on comment events).

via David Cramer.

--

View Session [Sentry]

Every GitHub, Sentry, Vercel, and Linear event now sets its scope values
in trusted data, and each one is a declared filter with an events list.
Scope values use the same case as identifiers, so a saved identifier can
later become exact filters. Core triggers, identifiers, watches, and
matching do not change.

Each event kind builds its data through one Zod schema. The shared
filter-event test helper now checks each value against the filter kind,
enum, and list.

Refs #2362

Co-Authored-By: David Cramer <david@sentry.io>
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 30, 2026 1:33am UTC

Request Review

@sentry-evals

sentry-evals Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Sentry Evals

junior-behavioral

Run Status Passed Failed Errored Cost Tokens Duration
Head Failed 81 2 0 $1.56 0 451.17s

Base commit 2236380 has no eval run to compare against.

junior-guardian

Run Status Passed Failed Errored Cost Tokens Duration
Head Passed 94 0 0 $0.03 0 28.13s

Base commit 2236380 has no eval run to compare against.

junior-integration

Run Status Passed Failed Errored Cost Tokens Duration
Head Passed 102 0 0 $0.90 0 292.89s

Base commit 2236380 has no eval run to compare against.

Sentry issue events declare an issueId filter, and Linear issue events
declare issueIdentifier and build their data with a strict schema. Sentry
events drop org, because one integration belongs to one org.

GitHub release tags are no longer URI-encoded in data; only ASCII letters
are lowercased. A blank tag is skipped. Deployment environments are trimmed
like the identifier. Ready-for-review data and lookup values go through
strict schemas. Vercel filters list their events.

A shared test helper checks, in each plugin, that every identifier turns
into filters that match its own events and no others.

Co-Authored-By: David Cramer <david@sentry.io>
@github-actions

Copy link
Copy Markdown
Contributor

106 screenshot changes — 0 changed · 106 added · 0 removed

Review screenshots in Frameshift

@dcramer
dcramer marked this pull request as ready for review October 30, 2026 01:38
@github-actions github-actions Bot added the risk: high PR risk score: high label Oct 30, 2026
@dcramer
dcramer merged commit 0ba63a0 into main Oct 30, 2026
52 checks passed
@dcramer
dcramer deleted the feat/event-scope-filters branch October 30, 2026 02:01

This branch was successfully deployed

1 active deployment
Preview – junior-docs — da00392a Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant