Workflow Trigger Processing#
Overview#
The WorkflowManager handles two fundamentally different code paths for triggering workflows: one for alert events and one for incident events. They differ significantly in how conditions are evaluated and how workflows are selected.
Alert Triggers: insert_events()#
Alert-triggered workflows go through WorkflowManager.insert_events(), which:
- Fetches all enabled tenant workflows from the store.
- For each workflow, iterates over its triggers and skips any that are not type
alert. - Evaluates CEL conditions against the alert payload :
- Legacy
filtersformat is first converted to a CEL expression via_convert_filters_to_cel(). - The
sourcefield receives special handling:source == "x"is rewritten tosource.contains("x")becausesourceis a list . - Severity strings are normalized to numeric order values before CEL evaluation .
- CEL is preprocessed via
preprocess_cel_expression()for additional normalization .
- Legacy
- If the trigger has neither
filtersnorcel, the workflow runs unconditionally . - After CEL passes, checks
only_on_changeandseverity_changedguards against the previous alert . - Enriches the alert from the DB before enqueuing .
- Appends to
scheduler.workflows_to_rununder a threading lock .
Entry points that call insert_events():
- Single-alert enrichment via
_enrich_alert()when status changes. - Bulk enrichment via
batch_enrich_alerts().
Incident Triggers: insert_incident()#
Incident-triggered workflows go through WorkflowManager.insert_incident(), which:
- Fetches all tenant workflows (no
exclude_disabledflag β disabled workflows are filtered inline) . - Collects all event strings from triggers of type
incident: e.g.,"created","updated","deleted". - Checks if the incoming
triggerstring is in that list β no CEL evaluation occurs . - Applies any stored incident enrichments to the
IncidentDtobefore enqueuing . - Appends to
scheduler.workflows_to_run.
Missing CEL Evaluation in insert_incident()#
Unlike alert triggers, incident triggers have no CEL filter support. Any cel or filters key in an incident trigger definition is silently ignored β the workflow fires for all incidents of that event type. This is a known gap: users cannot filter incident-triggered workflows by incident properties (e.g., severity, name, or custom enrichment fields).
Entry points that call insert_incident() via IncidentBl.send_workflow_event():
| Call site | Action sent |
|---|---|
IncidentBl.create_incident() | "created" |
IncidentBl.__postprocess_incident_change() | "updated" |
IncidentBl.__postprocess_alerts_change() | "updated" |
IncidentBl.delete_incident() | "deleted" |
PR #5254 (open) adds an alert_association_changed event that fires when alerts are linked/unlinked from an incident, and also populates incident.linked_alerts as a formatted list for use in workflow actions.
Known Bug: Incident Enrichment Fails in foreach Loops#
When an incident-triggered workflow iterates over a list (e.g., foreach: "{{ incident.services }}") and an action tries to enrich_incident, the enrichment fails with "No fingerprint found for alert enrichment".
Root cause (base_provider._enrich()): The foreach_context branch is entered first (line 212) because the loop value is a truthy string. The code then falls through all isinstance checks and reaches else: fingerprint = None, never reaching the incident_context branch at line 245. Fix: add a self.context_manager.incident_context check inside the foreach branch before the final else.
Key Files#
| File | Purpose |
|---|---|
workflowmanager.py | Core trigger dispatch: insert_events() (alert) and insert_incident() (incident) |
incidents_bl.py | Calls send_workflow_event() on every incident state change |
base_provider.py | _enrich() method where foreach+incident context bug resides |
cel_utils.py | preprocess_cel_expression() used by alert trigger CEL evaluation |