public WorkflowModel decide(WorkflowModel workflow) {
        if (workflow.getStatus().isTerminal()) {
            if (!workflow.getStatus().isSuccessful()) {
                cancelNonTerminalTasks(workflow);
            }
            return workflow;
        }

        // we find any sub workflow tasks that have changed
        // and change the workflow/task state accordingly
        adjustStateIfSubWorkflowChanged(workflow);

        try {
            DeciderService.DeciderOutcome outcome = deciderService.decide(workflow);
            if (outcome.isComplete) {
                endExecution(workflow, outcome.terminateTask);
                return workflow;
            }

            List<TaskModel> tasksToBeScheduled = outcome.tasksToBeScheduled;
            setTaskDomains(tasksToBeScheduled, workflow);
            List<TaskModel> tasksToBeUpdated = outcome.tasksToBeUpdated;

            tasksToBeScheduled = dedupAndAddTasks(workflow, tasksToBeScheduled);

            boolean stateChanged = scheduleTask(workflow, tasksToBeScheduled); // start

            for (TaskModel task : outcome.tasksToBeScheduled) {
                executionDAOFacade.populateTaskData(task);
                if (systemTaskRegistry.isSystemTask(task.getTaskType())
                        && NON_TERMINAL_TASK.test(task)) {
                    WorkflowSystemTask workflowSystemTask =
                            systemTaskRegistry.get(task.getTaskType());
                    if (!workflowSystemTask.isAsync()
                            && workflowSystemTask.execute(workflow, task, this)) {
                        tasksToBeUpdated.add(task);
                        stateChanged = true;
                    }
                }
            }

            if (!outcome.tasksToBeUpdated.isEmpty() || !tasksToBeScheduled.isEmpty()) {
                executionDAOFacade.updateTasks(tasksToBeUpdated);
            }

            if (stateChanged) {
                return decide(workflow);
            }

            if (!outcome.tasksToBeUpdated.isEmpty() || !tasksToBeScheduled.isEmpty()) {
                executionDAOFacade.updateWorkflow(workflow);
            }

            return workflow;

        } catch (TerminateWorkflowException twe) {
            LOGGER.info("Execution terminated of workflow: {}", workflow, twe);
            terminate(workflow, twe);
            return workflow;
        } catch (RuntimeException e) {
            LOGGER.error("Error deciding workflow: {}", workflow.getWorkflowId(), e);
            throw e;
        }
    }
@param workflow the workflow to evaluate the state for
@return true if the workflow has completed (success or failed), false otherwise. Note: This
    method does not acquire the lock on the workflow and should ony be called / overridden if
    No locking is required or lock is acquired externally
core/src/main/java/com/netflix/conductor/core/execution/WorkflowExecutor.java