Constraint Kinds
Quell's rule engine categorizes requirements into constraint kinds. Each kind has a deterministic rule for generating a test and injecting a violation.
Range constraints
Numeric bounds — gt, ge, lt, le, positive, negative:
Field(gt=0) # must be greater than 0
Field(ge=1, le=100) # must be between 1 and 100
if amount <= 0: raise ValueError
Violation: inject a value that violates the bound (e.g., amount=0 for gt=0).
Null guards
Optional values and None checks:
Optional[str]
if value is None: raise ValueError
Violation: pass None where the function expects a non-None value.
String constraints
Length, format, pattern:
Field(min_length=1, max_length=255)
Field(pattern=r"^[A-Z]{3}$")
if not email.contains("@"): raise ValueError
Violation: pass a string that violates the constraint.
Type errors
Wrong type passed to a function:
def process(amount: float) -> None
Violation: pass "not_a_float" or None where a float is expected.
Enum violations
Invalid enum member:
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
Violation: pass a string not in the enum.
Uniqueness constraints
Duplicate values in a collection:
Field(unique_items=True)
Violation: pass a list with duplicate items.
Cannot handle (→ LLM or SCAFFOLDED)
The rule engine doesn't handle:
- Requirements involving multiple interacting fields
- Requirements that depend on external state
- Complex business logic conditions
- Non-deterministic outcomes
These cases go to the LLM fallback (if enabled) or become SCAFFOLDED.