Skip to content

fix(alerts): create and edit issue alert rules via org-scoped /workflows/ - #1223

Merged
jared-outpost[bot] merged 5 commits into
mainfrom
fix/cli-issue-alerts-create-edit-workflows
Aug 1, 2026
Merged

fix(alerts): create and edit issue alert rules via org-scoped /workflows/#1223
jared-outpost[bot] merged 5 commits into
mainfrom
fix/cli-issue-alerts-create-edit-workflows

Conversation

@betegon

@betegon betegon commented Jul 10, 2026

Copy link
Copy Markdown
Member

What & why

Final slice of migrating issue alerts off the deprecated (HTTP 410 brownout) project-scoped /projects/{org}/{project}/rules/ endpoint (#1182), after read (#1215) and delete (#1216). This migrates create and edit.

create/edit now go through the @sentry/api SDK (createOrganizationWorkflow / updateOrganizationWorkflow), assembling the workflow-document body:

  • triggers: { logicType, conditions } from --condition / --action-match
  • actionFilters: [{ logicType, conditions, actions }] from --filter / --filter-match / --action
  • config.frequency from --frequency
  • detectorIds resolved from the project's error detector (an issue alert has to attach to it to fire) via listOrganizationDetectors(?project=…&query=type:error)

--action-match / --filter-match map to DataConditionGroup logic types the same way the backend dual-write does (anyany-short, allall). edit reads the workflow document (getOrganizationWorkflow), applies the flag overrides in workflow shape (--statusenabled), and PUTs it back.

The SDK types conditions/actions as unknown[] and its body fields as snake_case, but the endpoint's CamelSnakeSerializer wants camelCase — so the body is built as a record and cast into the typed SDK arg, the same double-cast pattern as getProject. No backend change needed.

Breaking change

--condition / --action / --filter now take workflow-native JSON ({type, comparison, conditionResult} / {type, data, config}) instead of the legacy rule identifiers ({id: "sentry.rules..."}). The --help examples are updated accordingly.

Scope

Read/delete (merged in #1215/#1216) use apiRequestToRegion; create/edit here use the @sentry/api SDK + cast, matching the projects-domain SDK adoption (#1213). Minor inconsistency within alerts.ts, intentional — this is the SDK-adoption direction.

Test plan

  • tsc --noEmit + biome check: clean.
  • vitest run test/lib/api/alerts.test.ts test/commands/alert: green (19 tests). API-level tests pin the exact workflow create/update/detector requests via mockFetch; command tests mock the api-client so they are transparent to the mechanism.
  • Not yet run live. The unit tests mock the API, so three things still need a real create/edit to confirm: (1) the edit read-modify-write round-trips through the workflows PUT, (2) config.frequency unit (minutes, per the CLI flag), (3) query=type:error returns the project error detector.

Refs #1182

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-01 17:09 UTC

@github-actions

Copy link
Copy Markdown
Contributor

Codecov Results 📊

✅ Patch coverage is 88.10%. Project has 5324 uncovered lines.
✅ Project coverage is 81.86%. Comparing base (base) to head (head).

Files with missing lines (1)
File Patch % Lines
src/commands/alert/issues/edit.ts 78.26% ⚠️ 5 Missing and 10 partials
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    81.81%    81.86%    +0.05%
==========================================
  Files          423       423         —
  Lines        29343     29356       +13
  Branches     19118     19135       +17
==========================================
+ Hits         24006     24032       +26
- Misses        5337      5324       -13
- Partials      1989      1991        +2

Generated by Codecov Action

@BYK BYK added the jared Trigger the Jared agent to work on stuff label Aug 1, 2026
@BYK

BYK commented Aug 1, 2026

Copy link
Copy Markdown
Member

Jared, get this ready to be merged, mark as ready, wait for all bot reviews, address all review comments then merge.

…ows/

Final slice of migrating issue alerts off the deprecated (HTTP 410
brownout) project-scoped /projects/{org}/{project}/rules/ endpoint
(#1182), after read (#1215) and delete (#1216). This migrates create and
edit.

create/edit now go through the @sentry/api SDK (createOrganizationWorkflow
/ updateOrganizationWorkflow), assembling the workflow-document body:
triggers {logicType, conditions}, actionFilters[{logicType, conditions,
actions}], config.frequency, and detectorIds resolved from the project's
error detector. --action-match/--filter-match map to logic types the same
way the backend dual-write does (any -> any-short, all -> all).

The SDK types conditions/actions as unknown[] and its body fields as
snake_case, but the endpoint's CamelSnakeSerializer wants camelCase, so
the body is built as a record and cast into the typed SDK arg (same
double-cast pattern as getProject). No backend change needed.

BREAKING CHANGE: --condition/--action/--filter now take workflow-native
JSON ({type, comparison, conditionResult} / {type, data, config}) instead
of legacy rule identifiers ({id: 'sentry.rules...'}).

Refs #1182
@jared-outpost
jared-outpost Bot force-pushed the fix/cli-issue-alerts-create-edit-workflows branch from 9bc01f0 to 1a135fb Compare August 1, 2026 10:37
@jared-outpost
jared-outpost Bot marked this pull request as ready for review August 1, 2026 10:42
@jared-outpost

jared-outpost Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

rebased onto current main and marked ready. the branch had diverged from main's history after the packages/cli/ monorepo move, so i re-applied the create/edit diff onto the current layout — the read/delete migrations (#1215/#1216) had already landed the workflow detector/unwrap helpers, so the only real conflicts were the @sentry/api import union in api/alerts.ts and the test suite (kept both the metric-alert and new workflow describe blocks).

green locally (tsc, biome, 27 alert tests) and in CI (unit tests, lint & typecheck, warden, semgrep, secret scan). waiting on bot reviews now.

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Aug 1, 2026
Comment thread packages/cli/src/commands/alert/issues/edit.ts
The workflows update endpoint returns `enabled` instead of `status`, but
`edit` spread the raw response so human output showed `undefined` and JSON
dropped `status`. Mirror the create path's enabled->status mapping.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

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 1 potential issue.

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 1db256c. Configure here.

Comment thread packages/cli/src/commands/alert/issues/create.ts
@jared-outpost

jared-outpost Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

status on the "address bot reviews then merge" request:

  • rebased onto current main and marked ready — green CI (unit tests, lint & typecheck, warden, semgrep, secret scan, dependency review).
  • Bugbot medium finding (edit output dropped status): fixed in 1db256cf, thread resolved.
  • Bugbot high finding (--action-match alltriggers.logicType: all): left open for @betegon. it contradicts the PR's documented dual-write mapping (allall), and flipping it to any-short blindly would invert AND→OR semantics — a worse bug if the backend accepts all. this needs a backend-contract call i can't make from the CLI repo.

not merging yet: i don't want to auto-merge over an unresolved high-severity correctness question that turns on the author's backend knowledge. @betegon once you confirm whether the workflows create endpoint requires any-short for trigger groups, i'll either apply the trigger-only special-case or we merge as-is.

The org-scoped workflows create endpoint rejects a trigger group that
carries an issue-alert trigger condition (first_seen_event /
regression_event / reappeared_event / issue_resolved_trigger) unless its
logic type is 'any-short' (BaseDataConditionGroupValidator._validate_logic_type
in getsentry/sentry). We were mapping --action-match all -> logicType 'all'
on the trigger group, so create would fail for the standard issue-alert
triggers. The dual-write in the backend preserves 'all' via .objects.create(),
which bypasses this validator — those groups are explicitly grandfathered,
so the CLI's endpoint path can't rely on that.

Split the mapping: triggerLogicType() always returns 'any-short' for the
trigger ("when") group; matchToLogicType() still maps --filter-match for the
action-filter ("if") group, which never holds trigger conditions and accepts
either logic type. edit does the same so it never writes a non-any-short
trigger. --action-match stays for parity but no longer affects the trigger.
@jared-outpost
jared-outpost Bot requested a review from BYK August 1, 2026 16:19
@BYK

BYK commented Aug 1, 2026

Copy link
Copy Markdown
Member

Fix: triggerLogicType() always returns any-short for the trigger group; matchToLogicType() still maps --filter-match for the action-filter group (which never holds trigger conditions and accepts either logic type). edit does the same. --action-match stays for parity but no longer affects the trigger; help text updated. Tests updated to assert the trigger is any-short regardless of --action-match.

Jared, I'm a bit confused. Does this mean we essentially removed the AND path from the product?

@jared-outpost

jared-outpost Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

No AND capability is lost — the AND that users actually rely on lives on the filter group, which this PR leaves fully intact.

Two separate match modes on an issue alert:

  • triggers ("when") — controlled by `--action-match`. These are lifecycle events: `first_seen_event`, `regression_event`, `reappeared_event`. They are mutually-exclusive states of a single event, so ANDing them always evaluates false — it never fires. The backend treats `all` here as degenerate: the workflows create validator outright rejects a non-`any-short` trigger group, and the legacy→workflow bridge (`format_request_data` in `project_rules.py`) already hardcodes the trigger group to `any-short` and discards `action_match`. So the product itself already ignores `--action-match all` for triggers.
  • filters ("if") — controlled by `--filter-match`, e.g. `level:error` AND `assigned_to:me` AND `event_frequency > N`. This is the real AND path, and it is untouched: `--filter-match all|any|none` still maps straight through to the action-filter group.

So the split is: --filter-match keeps all/any/none (the meaningful AND/OR/NONE), and --action-match is forced to any-short on the trigger group to match what the backend accepts and does anyway. The only thing that changed is we stopped sending a trigger logic type the create endpoint would 400 on.

(One caveat for full honesty: --action-match is now effectively a no-op on the CLI. If you would rather I drop the flag entirely rather than keep it "for parity," say the word and I will — I kept it to avoid a breaking flag removal.)

@BYK

BYK commented Aug 1, 2026

Copy link
Copy Markdown
Member

Jared,

(One caveat for full honesty: --action-match is now effectively a no-op on the CLI. If you would rather I drop the flag entirely rather than keep it "for parity," say the word and I will — I kept it to avoid a breaking flag removal.)

Yeah we should definitely drop it. Is this a sentry-cli v3 compatibility issue that we should note in our docs or just a breaking change with the former version of this command which did not exist in the legacy sentry-cli one?

Since the trigger group is always 'any-short' for issue alerts,
--action-match had no effect on the workflows body — it was accepted (and,
on create, required) but silently ignored. Remove it entirely: drop the
flag, its -m alias, the required-flag guard on create, and the type/help
references. The trigger group's logicType is now pinned to any-short
whenever conditions are set. --filter-match (all/any) on the action-filter
group — the meaningful AND/OR control — is unchanged.

Also refresh the alert docs fragment: the create example dropped
--action-match and switched to workflow-native condition/action JSON to
match this command's post-migration surface.
@jared-outpost

jared-outpost Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Done — dropped --action-match (and its -m alias) from both create and edit in 8323c70. On create it was even a required flag that did nothing, so this also removes a papercut. --filter-match (the real all/any control) is untouched, and the trigger group is pinned to any-short whenever conditions are set. Docs fragment + generated skill updated too.

On the compat question: this is not a sentry-cli v3 / legacy-CLI concern. The legacy Rust sentry-cli had no alert issues create/edit at all — this whole command group is new in the TS rewrite and has never shipped in a release (0.40.0-dev.0). So there is no v2→v3 migration story to document; --action-match never existed for any released CLI. It is only a breaking change to this command's own unreleased flag surface, which a one-line changelog entry for the release covers — no separate deprecation/migration note needed.

@jared-outpost

jared-outpost Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

fix-ci: attempt 1 — the failure is the e2e library.test.ts bundle build hanging (Bundle not built), a flaky infra timeout: the pnpm run bundle subprocess stalled ~3min with no output and no Bundle failed with exit code line (a real compile error would print one). Same e2e job passed on the prior commit 5b858e8, and this change only removes a CLI flag — it does not touch the SDK bundle. Re-running the failed jobs.

@BYK

BYK commented Aug 1, 2026

Copy link
Copy Markdown
Member

Shall we also alias --filter-match to -m shorthand now? You can merge after this.

The -m short was freed up when --action-match was dropped; point it at
--filter-match, the remaining match-mode flag. Regenerated skill docs.
@jared-outpost

jared-outpost Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Done — -m now aliases --filter-match on both create and edit (83ae6c6), skill docs regenerated. Enabling auto-merge (squash) so it lands once the new commit's checks pass.

@jared-outpost
jared-outpost Bot enabled auto-merge (squash) August 1, 2026 16:59
Comment on lines +123 to +126
const actionFilters = Array.isArray(body.actionFilters)
? (body.actionFilters as Record<string, unknown>[])
: [];
const filter = (actionFilters[0] as Record<string, unknown>) ?? {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: Partially editing an issue alert can overwrite and destroy the existing filter configuration if the API response for the alert rule is missing the actionFilters array.
Severity: HIGH

Suggested Fix

Before mutating actionFilters, validate that it was successfully loaded from the document returned by getIssueAlertWorkflowDocument. If actionFilters is missing from the API response, ensure the user's partial edits are merged with the existing state rather than creating a new, incomplete object. This will prevent accidental data loss during partial updates.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/cli/src/commands/alert/issues/edit.ts#L123-L126

Potential issue: When a partial edit is applied to an issue alert, such as using only
the `--action` flag, the code may construct a new, incomplete filter object. This occurs
if the response from `getIssueAlertWorkflowDocument` does not include an `actionFilters`
array. The code at lines 123-126 defaults to an empty object (`{}`) in this scenario. A
subsequent PUT request then replaces the entire existing action filter with this
incomplete object, silently removing other configured properties like `logicType` and
`conditions` from the alert rule, effectively destroying the filter configuration.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Jared, follow up with this

@jared-outpost
jared-outpost Bot merged commit c52f457 into main Aug 1, 2026
32 checks passed
@jared-outpost
jared-outpost Bot deleted the fix/cli-issue-alerts-create-edit-workflows branch August 1, 2026 17:08
@BYK BYK added jared Trigger the Jared agent to work on stuff and removed jared Trigger the Jared agent to work on stuff labels Aug 1, 2026
@BYK

BYK commented Aug 1, 2026

Copy link
Copy Markdown
Member

Jared address the overlooked Seer comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jared Trigger the Jared agent to work on stuff risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants