fix: include node type in plugin condition/action payload - #345
Conversation
pluginNodePayload previously omitted the automation node's own Type, so a plugin binary that registers handlers for more than one Condition or Action node type had no way to know which one the host was asking it to evaluate/run - it could only guess from the config shape, which is fragile and breaks down when two node types share overlapping config fields. Adds node_type to the JSON payload sent to plugins via EvaluateCondition/RunAction, alongside the existing config and task fields. Updates both call sites (walkPluginCondition, runPluginAction). Paired with the plugin-sdk-go change that adds NodeType to ConditionRequest/ActionRequest and dispatches plugin-side handlers by node type (see Paca-AI/plugin-sdk-go#<branch: feat/automation-condition-action-nodes>). Verified: go build ./... clean on services/api; go test passes on internal/worker, internal/service/automation, internal/domain/plugin, internal/platform/plugin.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Adds
node_typeto plugin condition/action payload —pluginNodePayloadnow includes the node's Type field so a plugin declaring multiple node types can dispatch internally. BothwalkPluginConditionandrunPluginActioncall sites are updated.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
- Renamed test functions to reflect the new applyUpdateTask method for assigning, setting status, priority, tags, and custom fields. - Updated assertions and error messages in tests to align with the new method. - Introduced a new test consumer with Redis for handling plugin trigger events. - Enhanced fakePluginRuntime to capture payloads dispatched to EvaluateCondition and RunAction. - Added tests for evaluating plugin conditions against tasks with various match modes. - Implemented tests for handling plugin trigger events, ensuring correct execution and task resolution.
There was a problem hiding this comment.
Important
The old action types and their ActionConfig fields are removed without a migration or compatibility path — see the cross-cutting issue below.
Reviewed changes since prior review (584fa21)
- Consolidated action types — Replaced 5 single-field built-in actions (
assign,set_status,set_priority,add_tag,set_custom_field) with a singleActionUpdateTaskbacked byTaskFieldUpdate, letting one node update any combination of task fields at once. - Added 7 new condition leaf fields — Title, story points, sprint, parent task, reporter, start date, and due date are now usable in built-in condition branches, with operator sets matching each field's type (date comparisons,
containsfor title, etc.). - Added plugin-emitted trigger event processing — A plugin's
event_emitcall now appends toStreamPluginTriggerEventswhen its topic matches a loaded plugin's declared trigger, andAutomationConsumerreads it alongside the existing task-activity and external-trigger streams, resolving project + task from the event payload. - Added plugin condition target/match-mode — Plugin condition nodes now support task-target resolution (
self,children,related) and match-mode aggregation (any/all), the same way built-in condition leaves do, via the newevaluatePluginConditionAgainstTasks. - Extended
pluginNodePayload— Now includesproject_idalongsidenode_type, so a plugin knows its project scope without requiring a user-enteredproject_idin config. - Schema-driven plugin config forms — Frontend config panel renders plugin-contributed node configs from the plugin manifest's JSON Schema, replacing the raw JSON textarea for plugin types that declare a schema.
- Plugin condition edge validation —
PluginConditionTrueHandlemoved to the domain package andvalidateEdgeHandlenow properly gates plugin condition edges to exactly"true"or"else".
⚠️ Old action types removed with no migration path
The five prior built-in action types — assign, set_status, set_priority, add_tag, set_custom_field — and their corresponding ActionConfig fields (status_id, importance, tag, field_key, value) are deleted in this diff. Any existing automation node saved with one of these types will:
- Fail validation on save —
validateActionConfigno longer has cases for these types, and the plugin resolver won't recognize them, sovalidateNodeTypeAndConfigrejects them. - Fail at execution time —
applyActionForTask'sdefaultbranch dispatches unknown types torunPluginAction, which won't resolve them.
If there are existing automations in the database with these action types, this is a hard break. If not (e.g., this feature hasn't reached a deployment with real data yet), the change is safe but the commit message understates its scope — the PR description still says this is about adding node_type to plugin payloads.
Technical details
# Old action type removal
## Affected sites
- `services/api/internal/domain/automation/entity.go` — old `ActionType` constants and `ValidBuiltinActionTypes` entries removed; old `ActionConfig` fields replaced by `Update *TaskFieldUpdate`
- `services/api/internal/service/automation/automation_service.go` — old `validateActionConfig` cases removed
- `services/api/internal/worker/automation_consumer.go` — old `applyAssign`/`applySetStatus`/`applySetPriority`/`applyAddTag`/`applySetCustomField` methods removed
## Required outcome
- Either add a data migration that rewrites existing automation nodes from old action types to `update_task` (old `assign { member_id: X }` → `update_task { assignee_ids: [X] }`, etc.), or confirm no existing data uses these types and close this as intentional.
## Open questions for the human
- Do existing automations in any environment (prod, staging) use the old action types?
- Is the scope expansion deliberate, or should the action consolidation + plugin trigger event processing be separate PRs?DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes since prior review (5f62b42)
- Added migration for legacy action types — New
000029_migrate_legacy_automation_actions.sqlrewrites everyautomation_nodesrow carryingassign,set_status,set_priority,add_tag, orset_custom_fieldinto the equivalentupdate_taskwith structuredTaskFieldUpdateJSONB configs, directly addressing the prior review's primary concern. Theadd_tag→tagsconversion is explicitly documented as lossy (append semantics lost in favor of full replacement) with a rationale that leaving the node unrecognized is strictly worse. - Added date format enforcement across the frontend-backend boundary —
toDateInputValue/fromDateInputValuehelpers now convert between<input type="date">YYYY-MM-DD values and RFC 3339 strings forstart_date/due_datein both condition leaves andupdate_taskaction configs. Backend tests (TestConditionLeaf_Evaluate_DueDateComparisons,TestConditionLeaf_Evaluate_DateFieldRejectsNonRFC3339Value,TestApplyUpdateTask_DueDate_JSONRoundTrip) guard the contract thatcompareTimePtrrequires. - Fixed
applyUpdateTaskidempotency edge cases — Description comparison now usesbytes.Equalinstead of onlylen > 0; tag and assignee comparisons use order-insensitivesortedStrings/sortedUUIDshelper functions so a reordered slice correctly registers as a no-op. Added corresponding test coverage.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes since prior review (544ba5b)
- Updated e2e tests to use
update_taskaction type — Every e2e test that referenced the old single-field action types (add_tag,assign,set_status,set_priority,set_custom_field) now uses the consolidatedupdate_taskwith the correspondingTaskFieldUpdatefields. Conversions verified:add_tag→update.tags,assign→update.assignee_ids,set_status→update.status_id,set_priority→update.importance,set_custom_field→update.custom_fields. - Updated unit test assertions —
TestValidateEdgeHandle_PluginConditionSourcenow correctly acceptsPluginConditionTrueHandlefor plugin condition nodes (matching thevalidateEdgeHandlelogic change from a prior commit), andTestValidateActionConfig_AddTag_AcceptsTargetis renamed to_UpdateTask_AcceptsTargetwith the correspondingTaskFieldUpdateconfig. - Clarified
compareTimePtrflow — Adefault:branch comment makes explicit that the firstswitchonopis a fast path forOpIsEmpty/OpIsNotEmpty;OpEquals/OpNotEquals/OpGreaterThan/OpLessThanare handled below after parsing the target time.
DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

pluginNodePayload previously omitted the automation node's own Type, so a plugin binary that registers handlers for more than one Condition or Action node type had no way to know which one the host was asking it to evaluate/run - it could only guess from the config shape, which is fragile and breaks down when two node types share overlapping config fields.
Adds node_type to the JSON payload sent to plugins via EvaluateCondition/RunAction, alongside the existing config and task fields. Updates both call sites (evaluatePluginConditionAgainstTasks, runPluginAction).
Paired with the plugin-sdk-go change that adds NodeType to ConditionRequest/ActionRequest and dispatches plugin-side handlers by node type (see Paca-AI/plugin-sdk-go#<branch:
feat/automation-condition-action-nodes>).
Verified: go build ./... clean on services/api; go test passes on internal/worker, internal/service/automation, internal/domain/plugin, internal/platform/plugin. Added assertions in automation_consumer_test.go confirming node_type actually lands in the JSON dispatched to EvaluateCondition/RunAction, not just that dispatch succeeds.