Repository: PrefectHQ/prefect

Issue:
`manual_only` tasks won't run
Currently, because we have stopped using `start_tasks` in the agent, `manual_only` triggers are _always_ called, preventing the tasks from running.  `Resume` states should "trigger" (pun intended) a bypass of triggers.

**cc**: @jlowin 


Discussion:

Resolving patch:
diff --git a/src/prefect/triggers.py b/src/prefect/triggers.py
--- a/src/prefect/triggers.py
+++ b/src/prefect/triggers.py
@@ -5,7 +5,7 @@
 the state of preceding tasks.
 """
 from typing import Set
-
+from prefect import context
 from prefect.engine import signals, state
 
 
@@ -26,20 +26,18 @@ def all_finished(upstream_states: Set["state.State"]) -> bool:
 
 def manual_only(upstream_states: Set["state.State"]) -> bool:
     """
-    This task will never run automatically. It will only run if it is
-    specifically instructed, either by ignoring the trigger or adding it
-    as a flow run's start task.
-
-    Note this doesn't raise a failure, it simply doesn't run the task.
+    This task will never run automatically, because this trigger will 
+    always place the task in a Paused state. The only exception is if
+    the "resume" keyword is found in the Prefect context, which happens
+    automatically when a task starts in a Resume state.
 
     Args:
         - upstream_states (set[State]): the set of all upstream states
     """
-    raise signals.PAUSE('Trigger function is "manual_only"')
+    if context.get("resume"):
+        return True
 
-
-# aliases
-always_run = all_finished
+    raise signals.PAUSE('Trigger function is "manual_only"')
 
 
 def all_successful(upstream_states: Set["state.State"]) -> bool:
@@ -104,3 +102,7 @@ def any_failed(upstream_states: Set["state.State"]) -> bool:
             'Trigger was "any_failed" but none of the upstream tasks failed.'
         )
     return True
+
+
+# aliases
+always_run = all_finished


Test patch:
diff --git a/tests/engine/test_task_runner.py b/tests/engine/test_task_runner.py
--- a/tests/engine/test_task_runner.py
+++ b/tests/engine/test_task_runner.py
@@ -572,6 +572,15 @@ def test_manual_only_empty(self):
         new_state = TaskRunner(task).check_task_trigger(state=state, upstream_states={})
         assert new_state is state
 
+    def test_manual_passes_when_context_is_resume(self):
+        task = Task(trigger=prefect.triggers.manual_only)
+        state = Pending()
+        with prefect.context(resume=True):
+            new_state = TaskRunner(task).check_task_trigger(
+                state=state, upstream_states={1: Success()}
+            )
+        assert new_state is state
+
     def test_custom_trigger_function_raise(self):
         def trigger(states):
             1 / 0
@@ -1335,6 +1344,14 @@ def test_task_runner_converts_pause_signal_to_paused_state_for_manual_only_trigg
     assert "manual_only" in out.message
 
 
+def test_task_runner_passes_manual_only_trigger_when_resume_state_is_passed():
+    t1, t2 = SuccessTask(), SuccessTask(trigger=prefect.triggers.manual_only)
+    e = Edge(t1, t2)
+    runner = TaskRunner(t2)
+    out = runner.run(state=Resume(), upstream_states={e: Success(result=1)})
+    assert isinstance(out, Success)
+
+
 def test_task_runner_converts_pause_signal_to_paused_state_for_internally_raised_pauses():
     class WaitTask(Task):
         def run(self):
diff --git a/tests/test_triggers.py b/tests/test_triggers.py
--- a/tests/test_triggers.py
+++ b/tests/test_triggers.py
@@ -1,9 +1,17 @@
 import pytest
 
-from prefect import triggers
+from prefect import triggers, context
 from prefect.core.edge import Edge
 from prefect.engine import signals
-from prefect.engine.state import Failed, Pending, Retrying, Skipped, State, Success
+from prefect.engine.state import (
+    Failed,
+    Pending,
+    Retrying,
+    Skipped,
+    State,
+    Success,
+    Resume,
+)
 
 
 def generate_states(success=0, failed=0, skipped=0, pending=0, retrying=0) -> dict:
@@ -87,6 +95,18 @@ def test_manual_only_with_mixed_states():
         triggers.manual_only(generate_states(success=1, failed=1, skipped=1))
 
 
+def test_manual_only_with_resume_in_context():
+    """manual only passes when resume = True in context"""
+    with context(resume=True):
+        assert triggers.manual_only(generate_states(success=1, failed=1, skipped=1))
+
+
+def test_manual_only_with_resume_state():
+    """Passing a resume state from upstream should have no impact"""
+    with pytest.raises(signals.PAUSE):
+        triggers.manual_only({Success(), Resume()})
+
+
 def test_all_finished_with_all_success():
     assert triggers.all_finished(generate_states(success=3))