Skip to content

Link automation.action.* events to automation.triggered event#169

Open
tomerqodo wants to merge 7 commits intoqodo_claude_vs_qodo_base_link_automationaction_events_to_automationtriggered_event_pr2from
qodo_claude_vs_qodo_head_link_automationaction_events_to_automationtriggered_event_pr2
Open

Link automation.action.* events to automation.triggered event#169
tomerqodo wants to merge 7 commits intoqodo_claude_vs_qodo_base_link_automationaction_events_to_automationtriggered_event_pr2from
qodo_claude_vs_qodo_head_link_automationaction_events_to_automationtriggered_event_pr2

Conversation

@tomerqodo
Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#2

chrisguidry and others added 7 commits January 25, 2026 12:09
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>
@github-actions github-actions Bot added the docs label Mar 10, 2026
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Link automation.action.* events to automation.triggered event for improved traceability

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add automation_triggered_event_id field to link action events to triggering automation event
• Include automation-triggered-event related resource on all action events (.triggered, .executed,
  .failed)
• Refactor related_resources building to avoid duplication in fail() and succeed() methods
• Update all action tests to verify triggering-event and automation-triggered-event related
  resources
• Add documentation explaining automation event tracing and related resource links
Diagram
flowchart LR
  A["automation.triggered/<br/>automation.resolved event"] -->|automation_triggered_event_id| B["TriggeredAction"]
  B -->|includes related resource| C["automation.action.triggered"]
  B -->|includes related resource| D["automation.action.executed/<br/>automation.action.failed"]
  C -->|automation-triggered-event role| A
  D -->|automation-triggered-event role| A
  B -->|includes related resource| E["triggering-event"]
  C -->|triggering-event role| E
  D -->|triggering-event role| E
Loading

Grey Divider

File Changes

1. src/prefect/server/events/schemas/automations.py ✨ Enhancement +18/-0

Add automation_triggered_event_id field with validation

src/prefect/server/events/schemas/automations.py


2. src/prefect/server/events/actions.py ✨ Enhancement +25/-19

Include automation-triggered-event in related resources

src/prefect/server/events/actions.py


3. src/prefect/server/events/triggers.py ✨ Enhancement +27/-6

Pass automation_triggered_event_id to TriggeredAction

src/prefect/server/events/triggers.py


View more (13)
4. tests/events/server/actions/test_actions_service.py 🧪 Tests +267/-3

Add comprehensive tests for automation event linking

tests/events/server/actions/test_actions_service.py


5. tests/events/server/actions/test_calling_webhook.py 🧪 Tests +6/-0

Update webhook action test for triggering-event resource

tests/events/server/actions/test_calling_webhook.py


6. tests/events/server/actions/test_cancelling_flow_run.py 🧪 Tests +7/-1

Update cancel flow run test for triggering-event resource

tests/events/server/actions/test_cancelling_flow_run.py


7. tests/events/server/actions/test_changing_flow_run_state.py 🧪 Tests +7/-1

Update change flow run state test for triggering-event resource

tests/events/server/actions/test_changing_flow_run_state.py


8. tests/events/server/actions/test_pausing_resuming_deployment.py 🧪 Tests +14/-2

Update deployment pause/resume tests for triggering-event resource

tests/events/server/actions/test_pausing_resuming_deployment.py


9. tests/events/server/actions/test_pausing_resuming_work_pool.py 🧪 Tests +14/-2

Update work pool pause/resume tests for triggering-event resource

tests/events/server/actions/test_pausing_resuming_work_pool.py


10. tests/events/server/actions/test_pausing_resuming_work_queue.py 🧪 Tests +14/-2

Update work queue pause/resume tests for triggering-event resource

tests/events/server/actions/test_pausing_resuming_work_queue.py


11. tests/events/server/actions/test_resuming_flow_run.py 🧪 Tests +7/-1

Update resume flow run test for triggering-event resource

tests/events/server/actions/test_resuming_flow_run.py


12. tests/events/server/actions/test_running_deployment.py 🧪 Tests +6/-0

Update run deployment test for triggering-event resource

tests/events/server/actions/test_running_deployment.py


13. tests/events/server/actions/test_sending_notification.py 🧪 Tests +12/-0

Update notification action tests for triggering-event resource

tests/events/server/actions/test_sending_notification.py


14. tests/events/server/actions/test_suspending_flow_run.py 🧪 Tests +7/-1

Update suspend flow run test for triggering-event resource

tests/events/server/actions/test_suspending_flow_run.py


15. tests/events/server/triggers/test_service.py 🧪 Tests +2/-0

Update trigger service tests for automation_triggered_event_id

tests/events/server/triggers/test_service.py


16. docs/v3/concepts/automations.mdx 📝 Documentation +17/-0

Document automation event tracing and related resources

docs/v3/concepts/automations.mdx


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Mar 10, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

1. triggers.py lacks future annotations 📘 Rule violation ✓ Correctness
Description
src/prefect/server/events/triggers.py contains type annotations but does not include `from
__future__ import annotations` as the first import. This violates the required typing setup and can
lead to inconsistent annotation behavior across the codebase.
Code

src/prefect/server/events/triggers.py[R451-453]

+    source_actions: List[
+        Tuple[Optional[ReceivedEvent], ServerActionTypes, UUID | None]
+    ] = [
Evidence
PR Compliance ID 1 requires from __future__ import annotations as the first import in src/
Python files that contain type annotations. The file uses annotated types (e.g., `source_actions:
List[Tuple[...]]) but its header shows imports starting with import asyncio` and no future import.

AGENTS.md
src/prefect/server/events/triggers.py[1-18]
src/prefect/server/events/triggers.py[449-453]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/prefect/server/events/triggers.py` contains type annotations but is missing `from __future__ import annotations` as the first import.

## Issue Context
Compliance requires this future import in all `src/` Python files that contain type annotations.

## Fix Focus Areas
- src/prefect/server/events/triggers.py[1-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. automation_triggered_event_id uses field_validator 📘 Rule violation ✓ Correctness
Description
The new validation for automation_triggered_event_id uses a @field_validator while depending on
another field (triggering_event). This is cross-field validation and should be implemented with
@model_validator to avoid order-dependent behavior.
Code

src/prefect/server/events/schemas/automations.py[R772-780]

+    @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
Evidence
PR Compliance ID 8 requires cross-field validation to use @model_validator. The added validator
reads info.data.get("triggering_event") while validating automation_triggered_event_id, which is
a cross-field dependency.

AGENTS.md
src/prefect/server/events/schemas/automations.py[772-779]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`TriggeredAction.validate_automation_triggered_event_id` is implemented as a `@field_validator` but performs cross-field validation by depending on `triggering_event`.

## Issue Context
Compliance requires cross-field validation to use Pydantic v2 `@model_validator` to ensure correct sequencing and avoid reading partially-validated fields.

## 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


3. Fail event drops related 🐞 Bug ✓ Correctness
Description
In Action.fail, the code constructs related_resources (including automation-triggered-event
and triggering-event) but emits prefect.automation.action.triggered with
related=self._resulting_related_resources, omitting the new links and contradicting success-path
behavior.
Code

src/prefect/server/events/actions.py[R199-205]

                    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,
Evidence
Action.fail() appends both new related-resource links to related_resources, but then does not
use that list for the action.triggered event (it uses self._resulting_related_resources
instead). In contrast, Action.succeed() emits action.triggered with related=related_resources.
The updated tests assert action.triggered includes the triggering-event related resource even
when the action fails, which the current fail() implementation will not provide.

src/prefect/server/events/actions.py[155-214]
src/prefect/server/events/actions.py[266-296]
tests/events/server/actions/test_actions_service.py[153-191]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`Action.fail()` builds an augmented `related_resources` list (including `automation-triggered-event` and `triggering-event`) but emits the `prefect.automation.action.triggered` event with `related=self._resulting_related_resources`, dropping those links.

### Issue Context
This makes failure-path `action.triggered` events inconsistent with success-path behavior and with the updated tests expecting a `triggering-event` related resource.

### Fix Focus Areas
- src/prefect/server/events/actions.py[197-207]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Proactive actions validation crash 🐞 Bug ✓ Correctness
Description
TriggeredAction now forbids setting automation_triggered_event_id when triggering_event is
null, but triggers.act() sets automation_triggered_event_id from the generated automation
state-change event even for proactive firings where triggering_event can be null, causing
TriggeredAction creation/parsing to raise ValueError.
Code

src/prefect/server/events/schemas/automations.py[R763-780]

+    automation_triggered_event_id: UUID | None = Field(
+        default=None,
+        description=(
+            "The ID of the automation.triggered or automation.resolved event that "
+            "prompted this action, used to link automation.action.* events back to "
+            "the state change event"
+        ),
+    )
+
+    @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
Evidence
Proactive firings legitimately allow triggering_event=None. However,
create_automation_state_change_event() always assigns an event id (uuid7()), and
triggers.act() always computes and passes that id as automation_triggered_event_id for all
actions. The new validator then raises when automation_triggered_event_id is non-null and
triggering_event is null, which will break proactive-trigger automations (and the action consumer,
which parses TriggeredAction from the message).

src/prefect/server/events/schemas/automations.py[684-692]
src/prefect/server/events/schemas/automations.py[772-780]
src/prefect/server/events/schemas/automations.py[424-467]
src/prefect/server/events/triggers.py[430-483]
tests/events/server/triggers/test_service.py[64-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`TriggeredAction.automation_triggered_event_id` validation rejects the valid combination of `automation_triggered_event_id != None` with `triggering_event == None`, but `triggers.act()` sets `automation_triggered_event_id` from the generated automation state-change event id even for proactive firings where `triggering_event` may be null.

### Issue Context
This will raise during `TriggeredAction(...)` creation in `triggers.act()` and/or during message parsing in the actions consumer, breaking proactive-trigger automations.

### Fix Focus Areas
- src/prefect/server/events/schemas/automations.py[763-780]
- src/prefect/server/events/triggers.py[439-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +451 to +453
source_actions: List[
Tuple[Optional[ReceivedEvent], ServerActionTypes, UUID | None]
] = [
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. triggers.py lacks 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. This violates the required typing setup and can
lead to inconsistent annotation behavior across the codebase.
Agent Prompt
## Issue description
`src/prefect/server/events/triggers.py` contains type annotations but is missing `from __future__ import annotations` as the first import.

## Issue Context
Compliance requires this future import in all `src/` Python files that contain type annotations.

## Fix Focus Areas
- src/prefect/server/events/triggers.py[1-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +772 to +780
@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
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. automation_triggered_event_id uses field_validator 📘 Rule violation ✓ Correctness

The new validation for automation_triggered_event_id uses a @field_validator while depending on
another field (triggering_event). This is cross-field validation and should be implemented with
@model_validator to avoid order-dependent behavior.
Agent Prompt
## Issue description
`TriggeredAction.validate_automation_triggered_event_id` is implemented as a `@field_validator` but performs cross-field validation by depending on `triggering_event`.

## Issue Context
Compliance requires cross-field validation to use Pydantic v2 `@model_validator` to ensure correct sequencing and avoid reading partially-validated fields.

## 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

Comment on lines 199 to 205
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,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Fail event drops related 🐞 Bug ✓ Correctness

In Action.fail, the code constructs related_resources (including automation-triggered-event
and triggering-event) but emits prefect.automation.action.triggered with
related=self._resulting_related_resources, omitting the new links and contradicting success-path
behavior.
Agent Prompt
### Issue description
`Action.fail()` builds an augmented `related_resources` list (including `automation-triggered-event` and `triggering-event`) but emits the `prefect.automation.action.triggered` event with `related=self._resulting_related_resources`, dropping those links.

### Issue Context
This makes failure-path `action.triggered` events inconsistent with success-path behavior and with the updated tests expecting a `triggering-event` related resource.

### Fix Focus Areas
- src/prefect/server/events/actions.py[197-207]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +763 to +780
automation_triggered_event_id: UUID | None = Field(
default=None,
description=(
"The ID of the automation.triggered or automation.resolved event that "
"prompted this action, used to link automation.action.* events back to "
"the state change event"
),
)

@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
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Proactive actions validation crash 🐞 Bug ✓ Correctness

TriggeredAction now forbids setting automation_triggered_event_id when triggering_event is
null, but triggers.act() sets automation_triggered_event_id from the generated automation
state-change event even for proactive firings where triggering_event can be null, causing
TriggeredAction creation/parsing to raise ValueError.
Agent Prompt
### Issue description
`TriggeredAction.automation_triggered_event_id` validation rejects the valid combination of `automation_triggered_event_id != None` with `triggering_event == None`, but `triggers.act()` sets `automation_triggered_event_id` from the generated automation state-change event id even for proactive firings where `triggering_event` may be null.

### Issue Context
This will raise during `TriggeredAction(...)` creation in `triggers.act()` and/or during message parsing in the actions consumer, breaking proactive-trigger automations.

### Fix Focus Areas
- src/prefect/server/events/schemas/automations.py[763-780]
- src/prefect/server/events/triggers.py[439-488]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants