-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Problem
The workflow node model now supports an optional outcome field to track completion sub-states (e.g., "APPROVED", "REJECTED", "EXPEDITED", "FAST_TRACKED") as part of the conditional unlock configuration feature (#174).
However, the TaskManager plugin system is not wired to populate this field. Currently:
plugin.ExecutionResponsehas noOutcomefield, so plugins cannot return outcomesnotifyWorkflowManager()does not accept or pass theoutcomeparameterWorkflowManagerNotificationhas theOutcomefield but it's never populated- The workflow state machine cannot evaluate unlock conditions that depend on node outcomes
This means even though the state machine supports outcome-based unlocking, there's no mechanism for task plugins to produce and communicate outcomes back to the workflow manager.
Proposed Solution
Wire up the Outcome field through the plugin execution pipeline:
1. Update plugin.ExecutionResponse
Add optional Outcome field to allow plugins to return completion outcomes:
type ExecutionResponse struct {
NewState *State
ExtendedState *string
Outcome *string // NEW: Outcome sub-state for COMPLETED transitions (e.g., "APPROVED", "REJECTED")
AppendGlobalContext map[string]any
Message string
ApiResponse *ApiResponse
}2. Update TaskManager execute() method
Pass the outcome from ExecutionResponse to notifyWorkflowManager():
func (tm *taskManager) execute(ctx context.Context, activeTask *container.Container, payload *plugin.ExecutionRequest) (*plugin.ExecutionResponse, error) {
result, err := activeTask.Execute(ctx, payload)
if err != nil {
return nil, err
}
if result.NewState != nil {
tm.notifyWorkflowManager(ctx, activeTask.TaskID, result.NewState, result.ExtendedState, result.AppendGlobalContext, result.Outcome) // NEW: pass outcome
}
return result, nil
}3. Update notifyWorkflowManager() signature
Add outcome parameter and populate the notification:
func (tm *taskManager) notifyWorkflowManager(ctx context.Context, taskID uuid.UUID, state *plugin.State, extendedState *string, appendGlobalContext map[string]any, outcome *string) {
// ... existing code ...
notification := WorkflowManagerNotification{
TaskID: taskID,
UpdatedState: state,
ExtendedState: extendedState,
AppendGlobalContext: appendGlobalContext,
Outcome: outcome, // NEW: set outcome
}
// ... rest of implementation ...
}4. Update all notifyWorkflowManager() call sites
Two locations in manager.go need updating:
- Line ~272 (in
execute()) - Line ~286 (in container/manager interaction)
Add result.Outcome parameter to both calls.
5. Update Tests
Enhance manager_test.go to verify:
- Outcomes are properly passed through
ExecutionResponse→ notification - Nil outcomes don't break the pipeline
- Different outcome values (APPROVED, REJECTED, etc.) are correctly propagated
Alternatives
- Keep outcome in task plugin but don't propagate to workflow - Current state, but outcome information is lost before reaching state machine
- Use task extended_state for outcomes - Possible but conflates two concerns: extended error state vs. business outcome
- Add separate outcome notification channel - Over-engineered for current needs; reuse the existing notification mechanism
Acceptance Criteria
-
Outcome *stringfield added toplugin.ExecutionResponse -
notifyWorkflowManager()accepts and passesoutcomeparameter - All call sites updated to pass
result.Outcome -
WorkflowManagerNotification.Outcomeis populated during notification - Unit tests cover outcome propagation (happy path, nil outcome, various outcome values)
- No breaking changes to plugin API (field is optional)
- Existing plugins continue working unchanged
Files to Modify
./backend/internal/task/plugin/plugin.go- Add Outcome field to ExecutionResponse./backend/internal/task/manager/manager.go- Update execute() and notifyWorkflowManager()./backend/internal/task/manager/manager_test.go- Add outcome propagation tests
Implementation Notes
- Keep
Outcomeas optional (*string) for backward compatibility - Nil outcomes should be handled gracefully (existing behavior)
- Outcomes should propagate regardless of state value (but typically set on COMPLETED states)
- No database schema changes required (outcome already added in WorkflowNode in [BE][WM] Conditional Workflow Unlocking and End-Node Completion #174)
- Documentation: Update plugin developer guide to document when/how to set outcomes