Appendix C - 20. Typed `ViolationCategory` / `FailureCategory` enums
The Structure of Typed ViolationCategory / FailureCategory enums — its shape at a glance:
Producers and consumers speak the enum, not strings. External strings are mapped into the enum at one boundary. An exhaustiveness check lights up when a handler misses a case.
flowchart LR
Ext[/External strings/] -->|map at boundary| Enum["Typed category enum"]
Prod[Producers] --> Enum
Enum --> Cons[Consumers / handlers]
Cons --> X{{Exhaustiveness check}}
X -.->|missing case| Fail([compile error])
Accessible description: external strings are mapped into a typed category enum at one boundary; producers and consumers speak the enum. An exhaustiveness check over the handlers produces a compile error when a case is unhandled.
Projected from the catalogue entry repair-vocabulary / Typed ViolationCategory / FailureCategory enums.
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — Replace free-form failure strings with typed enums, so the categorical move-space is a closed, enumerable set that the compiler and lints can check for exhaustive handling.
Motivation
Bare failure strings ("timeout", "corrupt", "rate_limit") are typo-prone, un-enumerable, and let a switch silently miss a case. A typo compiles and mis-routes; a new category isn't forced into every handler; and you can't even ask "are all categories handled?" The failure is an unhandled or mistyped category → silent misbehaviour, recurring at every place a category is produced or consumed.
Applicability
- A closed, identifiable category set. The space must actually be enumerable.
- The enums, and callers switched from strings to enum values.
- Boundary mapping from external/serialized strings into the enum.
Structure
The Structure diagram appears at the top of this page.
Sample Code
An enum turns an open string vocabulary into a closed, checkable set. Callers compare against enum members, not raw strings, so a typo is a name error at author time. External strings arrive at one boundary and map in; an unmapped string fails loudly there rather than silently mis-routing downstream.
from enum import Enum
class FailureCategory(Enum):
TIMEOUT = "timeout"
CORRUPT = "corrupt"
RATE_LIMIT = "rate_limit"
_FROM_WIRE = {c.value: c for c in FailureCategory}
def parse_category(raw: str) -> FailureCategory:
"""The one boundary where external strings become the typed category.
An unknown string fails here, loudly — not three layers down as a silent miss."""
try:
return _FROM_WIRE[raw]
except KeyError:
raise ValueError(f"unknown failure category {raw!r}")
def handle(cat: FailureCategory) -> str:
# exhaustive match: adding a new enum member makes this branch incomplete,
# which a type-checker flags — the missing case can't stay silent
match cat:
case FailureCategory.TIMEOUT: return "retry"
case FailureCategory.CORRUPT: return "fail"
case FailureCategory.RATE_LIMIT: return "backoff"
Consequences
- Adding a category touches every handler — which is the point (it forces handling), but it is real work.
- Boundary translation. External strings (LLM output, wire formats) still arrive as strings and must be mapped in at a controlled seam, but a seam nonetheless.
Example use within DocAble
ViolationCategory/FailureCategoryenums in place of bare failure strings.- Enum-value comparison instead of regex-on-strings (the regex-usage discipline).
Related Patterns
- See also (sibling) — remediation-verbs, codemod-first: the other bounded-move-space constraints.
- See also (cross-target) — the agent side's const-string topic registry (typed-event-bus) is the same "typed namespace over free-form strings" move for event topics.
- Enables — typing a failure/violation space is the precondition for error-path enumeration (the testing strategy in the method's stance): you can only walk "every error edge" and ask "did we cover them all?" when the edges are a finite, named set, not ad-hoc strings.