A recorded batch failure carries structured details onto the sub_task outputs 🟥

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-07 11:49:56 +00:00
parent f01131a064
commit 8a3c0f3e77
2 changed files with 39 additions and 1 deletions

View file

@ -5,6 +5,19 @@ from typing import Any, Optional
from uuid import UUID, uuid4
class SubTaskFailure(Exception):
"""A *recorded* failure: the work ran, its outcome belongs on the SubTask
record, and the message must NOT be retried (ADR-0055 status is a
record, not retry fuel). Infra crashes raise anything else and keep their
retry semantics. ``details`` lands on the failed SubTask's outputs."""
def __init__(
self, message: str, details: Optional[dict[str, Any]] = None
) -> None:
super().__init__(message)
self.details = details
class SubTaskStatus(str, Enum):
WAITING = "waiting"
IN_PROGRESS = "in progress"

View file

@ -2,7 +2,7 @@ from uuid import uuid4
import pytest
from domain.tasks.subtasks import SubTask, SubTaskStatus
from domain.tasks.subtasks import SubTask, SubTaskFailure, SubTaskStatus
def test_create_subtask_starts_waiting() -> None:
@ -95,6 +95,31 @@ def test_complete_without_result_leaves_outputs_unset() -> None:
assert st.outputs is None
def test_fail_with_subtask_failure_records_structured_details() -> None:
# arrange
st = SubTask.create(task_id=uuid4())
st.start()
# act
st.fail(
SubTaskFailure(
"1 of 2 properties failed",
details={
"succeeded": 1,
"failed": [{"property_id": 9, "error": "degenerate prediction"}],
},
)
)
# assert
assert st.status is SubTaskStatus.FAILED
assert st.outputs == {
"error": "1 of 2 properties failed",
"succeeded": 1,
"failed": [{"property_id": 9, "error": "degenerate prediction"}],
}
def test_fail_records_error_in_outputs() -> None:
# arrange
st = SubTask.create(task_id=uuid4())