src/sentry/workflow_engine/docs/legacy_backport.md
Reimplement legacy alerts API endpoints using workflow engine abstractions. Backported code paths should not use legacy models (AlertRule, Rule, AlertRuleActivity, Incident, etc.) for their core logic.
Each legacy endpoint gets a parallel workflow engine implementation, gated behind feature flags. The broad flag organizations:workflow-engine-rule-serializers enables the workflow engine path for all backported endpoints. Per-endpoint-method flags allow independent rollout of individual code paths (see Feature Flag Strategy below). The two implementations live side-by-side in the same endpoint class; the non-workflow engine code is kept untouched as much as possible.
Read endpoints (GET) query workflow engine models (Detector, Workflow, DataSource, GroupOpenPeriod) and use dedicated serializers that reconstruct the legacy response shape.
Write endpoints (POST, PUT, DELETE) translate the legacy API request into a call to the existing workflow engine Validators, so that new data is single-written through the same validation and creation logic used by the native workflow engine APIs. The goal is to reuse, not reimplement, the write path.
Data that was created by the legacy system and migrated (or is being written to both systems simultaneously). Association tables bridge the two:
AlertRuleDetector — despite the name, this maps either a metric alert (alert_rule_id) or an issue alert (rule_id) to a Detector. Each row carries exactly one of the two (enforced by a check constraint).AlertRuleWorkflow — same pattern, mapping alert_rule_id or rule_id to a Workflow.IncidentGroupOpenPeriod — maps a legacy incident_id / incident_identifier to a GroupOpenPeriod.When the workflow engine path receives a real legacy ID (e.g. an alertRule query param), it resolves the corresponding workflow engine object via these tables.
Data created exclusively by the workflow engine with no legacy counterpart. These objects have no rows in the association tables. To maintain API compatibility, they are exposed with manufactured IDs. The helpers for this live in src/sentry/incidents/endpoints/serializers/utils.py:
get_fake_id_from_object_id(obj_id) — used by serializers to manufacture an ID for API responsesget_object_id_from_fake_id(fake_id) — used by endpoints to recover the real object ID from an incoming parameter. If non-positive, the input wasn't a valid manufactured ID.Endpoints that accept IDs as input must handle both real legacy IDs (via association tables) and manufactured IDs (via get_object_id_from_fake_id).
All endpoints decorated with @track_alert_endpoint_execution are in scope for backport:
Metric alert rules
OrganizationAlertRuleIndexEndpointOrganizationAlertRuleDetailsEndpointOrganizationCombinedRuleIndexEndpointProjectAlertRuleIndexEndpointProjectAlertRuleDetailsEndpointIncidents
OrganizationIncidentIndexEndpointOrganizationIncidentDetailsEndpointIssue alert rules
ProjectRulesEndpointProjectRuleDetailsEndpointProjectRuleEnableEndpointProjectRuleTaskDetailsEndpointSnooze
RuleSnoozeEndpointMetricRuleSnoozeEndpointorganizations:workflow-engine-rule-serializers enables all backported paths at once (useful for testing). Per-endpoint flags (e.g. organizations:workflow-engine-combinedruleindex-get) allow independent prod rollout — each is OR'd with the broad flag. Per-feature flags (e.g. organizations:workflow-engine-issue-alert-endpoints-post) allow a subset of endpoints scoped to a feature to be enabled to avoid a large number of individual flags. Naming convention: organizations:workflow-engine-{lowercaseendpointclass}-{method}.
Some legacy features can't or won't be supported in workflow engine models (e.g. AlertRule snapshots). Acknowledge these explicitly in code and tests where appropriate to make it clear which differences are known and intentional and which may be bugs.
known_differences set documenting expected divergences.