Repository: PrefectHQ/prefect

Issue:
PIN 16: Implement `ValidationFailed` State
## Use Case
See [PIN 16](https://github.com/PrefectHQ/prefect/pull/2134) for background. In particular, this blocks full implementation of `Result.validate`.


## Solution
Implement a new State, `ValidationFailed`, similar to [other Prefect states](https://github.com/PrefectHQ/prefect/blob/66703e5f0a2d0d035d4d48af013b87d24a7a2a56/src/prefect/engine/state.py), that can be utilized during Result validation.
- [x] add new state
- [x] add new signal
- [x] update state docs and diagram




## Alternatives
*Please describe any alternatives you've considered, even if you've dismissed them*



Discussion:

Resolving patch:
diff --git a/src/prefect/engine/signals.py b/src/prefect/engine/signals.py
--- a/src/prefect/engine/signals.py
+++ b/src/prefect/engine/signals.py
@@ -116,6 +116,21 @@ class TRIGGERFAIL(FAIL):
     _state_cls = state.TriggerFailed
 
 
+class VALIDATIONFAIL(FAIL):
+    """
+    Indicates that a task's result validation failed.
+
+    Args:
+        - message (Any, optional): Defaults to `None`. A message about the signal.
+        - *args (Any, optional): additional arguments to pass to this Signal's
+            associated state constructor
+        - **kwargs (Any, optional): additional keyword arguments to pass to this Signal's
+            associated state constructor
+    """
+
+    _state_cls = state.ValidationFailed
+
+
 class SUCCESS(PrefectStateSignal):
     """
     Indicates that a task succeeded.
diff --git a/src/prefect/engine/state.py b/src/prefect/engine/state.py
--- a/src/prefect/engine/state.py
+++ b/src/prefect/engine/state.py
@@ -810,6 +810,23 @@ class TriggerFailed(Failed):
     color = "#ff5131"
 
 
+class ValidationFailed(Failed):
+    """
+    Finished stated indicating failure due to failed result validation.
+
+    Args:
+        - message (str or Exception, optional): Defaults to `None`. A message about the
+            state, which could be an `Exception` (or [`Signal`](signals.html)) that caused it.
+        - result (Any, optional): Defaults to `None`. A data payload for the state.
+        - cached_inputs (dict): A dictionary of input keys to fully hydrated `Result`s.
+            Used / set if the Task requires retries.
+        - context (dict, optional): A dictionary of execution context information; values
+            should be JSON compatible
+    """
+
+    color = "#ff5131"
+
+
 class Skipped(Success):
     """
     Finished state indicating success on account of being skipped.
diff --git a/src/prefect/serialization/state.py b/src/prefect/serialization/state.py
--- a/src/prefect/serialization/state.py
+++ b/src/prefect/serialization/state.py
@@ -162,6 +162,11 @@ class Meta:
         object_class = state.TriggerFailed
 
 
+class ValidationFailedSchema(FailedSchema):
+    class Meta:
+        object_class = state.ValidationFailed
+
+
 class SkippedSchema(SuccessSchema):
     class Meta:
         object_class = state.Skipped
@@ -199,4 +204,5 @@ class StateSchema(OneOfSchema):
         "Success": SuccessSchema,
         "TimedOut": TimedOutSchema,
         "TriggerFailed": TriggerFailedSchema,
+        "ValidationFailed": ValidationFailedSchema,
     }


Test patch:
diff --git a/tests/engine/test_signals.py b/tests/engine/test_signals.py
--- a/tests/engine/test_signals.py
+++ b/tests/engine/test_signals.py
@@ -12,6 +12,7 @@
     SKIP,
     SUCCESS,
     TRIGGERFAIL,
+    VALIDATIONFAIL,
     PrefectStateSignal,
 )
 from prefect.engine.state import (
@@ -23,6 +24,7 @@
     State,
     Success,
     TriggerFailed,
+    ValidationFailed,
 )
 
 
@@ -85,6 +87,7 @@ def test_retry_signals_prefer_supplied_run_count_to_context():
     [
         (FAIL, Failed),
         (TRIGGERFAIL, TriggerFailed),
+        (VALIDATIONFAIL, ValidationFailed),
         (SUCCESS, Success),
         (PAUSE, Paused),
         (RETRY, Retrying),
diff --git a/tests/engine/test_state.py b/tests/engine/test_state.py
--- a/tests/engine/test_state.py
+++ b/tests/engine/test_state.py
@@ -31,6 +31,7 @@
     Success,
     TimedOut,
     TriggerFailed,
+    ValidationFailed,
     _MetaState,
 )
 from prefect.serialization.result_handlers import ResultHandlerSchema
@@ -383,6 +384,9 @@ def test_timedout_is_failed(self):
     def test_trigger_failed_is_failed(self):
         assert issubclass(TriggerFailed, Failed)
 
+    def test_validation_failed_is_failed(self):
+        assert issubclass(ValidationFailed, Failed)
+
 
 @pytest.mark.parametrize(
     "state_check",
@@ -410,6 +414,7 @@ def test_trigger_failed_is_failed(self):
         dict(state=Success(), assert_true={"is_finished", "is_successful"}),
         dict(state=TimedOut(), assert_true={"is_finished", "is_failed"}),
         dict(state=TriggerFailed(), assert_true={"is_finished", "is_failed"}),
+        dict(state=ValidationFailed(), assert_true={"is_finished", "is_failed"}),
     ],
 )
 def test_state_is_methods(state_check):