Link automation.action.* events to automation.triggered event#143
Conversation
When debugging automation failures at high volumes, it's difficult to trace from an `automation.action.failed` event back to the `automation.triggered` event that spawned it. This adds an `automation_triggered_event_id` field to `TriggeredAction` and includes it as a related resource with role `automation-triggered-event` on all action events (`.triggered`, `.executed`, and `.failed`). Port of https://github.com/PrefectHQ/nebula/pull/10911 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The triggering-event related resource should appear on all three action event types (.triggered, .executed, .failed) to enable full traceability. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Now that automation.action.executed and automation.action.failed events include a triggering-event related resource, update the tests to expect it in their assertions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Build related_resources once at the start of fail() and succeed() methods and reuse it for both the .triggered and .failed/.executed events, rather than building separate lists with identical logic. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add a brief section to the automations docs explaining the events emitted during automation execution and the related resources (triggering-event and automation-triggered-event) available for tracing action failures back to their source. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Code Review by Qodo
1. triggers.py missing future annotations
|
| source_actions: List[ | ||
| Tuple[Optional[ReceivedEvent], ServerActionTypes, UUID | None] | ||
| ] = [ |
There was a problem hiding this comment.
1. triggers.py missing future annotations 📘 Rule violation ✓ Correctness
• src/prefect/server/events/triggers.py contains type annotations but does not include `from __future__ import annotations` as the first import statement. • This violates the required typing pattern for files in src/ and can lead to inconsistent forward-reference behavior and slower type checking. • The new UUID | None annotation added in this PR increases the file’s reliance on consistent annotation handling.
Agent prompt
## Issue description
`src/prefect/server/events/triggers.py` uses type annotations but does not include `from __future__ import annotations` as the first import statement, violating the required typing standard.
## Issue Context
The file contains annotations (including the newly added `UUID | None` type usage) and must opt into future annotations per the compliance requirement.
## Fix Focus Areas
- src/prefect/server/events/triggers.py[1-18]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| @field_validator("automation_triggered_event_id") | ||
| @classmethod | ||
| def validate_automation_triggered_event_id(cls, v, info): | ||
| """Ensure automation_triggered_event_id is only set when triggering_event exists.""" | ||
| if v is not None and info.data.get("triggering_event") is None: | ||
| raise ValueError( | ||
| "automation_triggered_event_id can only be set when triggering_event is provided" | ||
| ) | ||
| return v |
There was a problem hiding this comment.
2. Cross-field check uses field_validator 📘 Rule violation ✓ Correctness
• TriggeredAction.validate_automation_triggered_event_id validates automation_triggered_event_id based on another field (triggering_event). • This is cross-field validation but is implemented with @field_validator, which violates the requirement to use @model_validator for multi-field dependent validation. • Keeping it as a field validator can create order/availability issues (relying on info.data) and makes validation sequencing less reliable.
Agent prompt
## Issue description
`TriggeredAction` performs cross-field validation (it checks `automation_triggered_event_id` based on `triggering_event`) using `@field_validator`, but cross-field validation must use `@model_validator`.
## Issue Context
The current implementation relies on `info.data.get("triggering_event")`, which is order-dependent and does not follow the required validation pattern.
## Fix Focus Areas
- src/prefect/server/events/schemas/automations.py[763-780]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| occurred=triggered_action.triggered, | ||
| event="prefect.automation.action.triggered", | ||
| resource=resource, | ||
| related=related_resources, | ||
| related=self._resulting_related_resources, | ||
| payload=action_details, | ||
| id=triggered_event_id, | ||
| follows=follows_id, |
There was a problem hiding this comment.
3. Fail drops related links 🐞 Bug ✓ Correctness
• In the failure path, the code builds related_resources with the new automation-triggered-event and existing triggering-event, but the emitted prefect.automation.action.triggered event does not use it. • This makes the most important trace links missing exactly when actions fail, and contradicts the updated tests/expectations for action.triggered. • Result: users cannot reliably trace failures back to the automation state change/original triggering event, and tests should fail.
Agent prompt
### Issue description
In the failure path, `Action.fail()` builds an enriched `related_resources` list but does not attach it to the emitted `prefect.automation.action.triggered` event, dropping trace links.
### Issue Context
This breaks the intended tracing behavior (and updated tests) for action-triggered events when the action fails.
### Fix Focus Areas
- src/prefect/server/events/actions.py[197-206]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Benchmark PR from qodo-benchmark#553