Policy Validation
Type mismatches, contradictions, and redundancies.
Callcium validates policies at build time through semantic analysis. When you call PolicyBuilder.build(), the PolicyValidator inspects every constraint for type errors, logical contradictions, redundancies, and vacuous conditions — then reverts if any issue is found.
Using validation
PolicyBuilder.build() validates by default and reverts on any issue:
// Reverts with ValidationError if issues are found.
bytes memory policy = PolicyBuilder
.create("approve(address,uint256)")
.add(arg(0).isIn(trustedSpenders))
.build();To inspect issues without reverting, use PolicyBuilder.validate():
Issue[] memory issues = PolicyBuilder
.create("approve(address,uint256)")
.add(arg(0).isIn(trustedSpenders))
.validate();
for (uint256 i = 0; i < issues.length; i++) {
// Handle each issue: severity, category, code, message.
}PolicyBuilder.buildUnsafe() skips validation entirely. The resulting policy may be invalid.
Issue structure
Each issue contains:
| Field | Type | Description |
|---|---|---|
severity | IssueSeverity | Error, Warning, or Info. |
category | IssueCategory | TypeMismatch, Contradiction, Redundancy, Vacuity, or Compatibility. |
code | bytes32 | Machine-readable identifier. |
groupIndex | uint256 | Index of the group holding the rule the issue names. |
ruleIndex | uint256 | Index of that rule within the group: a test, or the loop of a body test. |
bodyRuleIndex | uint256 | Index of the test within its loop's body; NO_INDEX for a root rule. |
scope | uint8 | Scope of the rule. |
path | bytes | Path of the rule; a body test's path starts at the element. |
operator | bytes | Encoded operator of a test; empty for a loop. |
value1 | bytes32 | First value that shows the defect, or zero. |
value2 | bytes32 | Second value that shows the defect, or zero. |
message | string | Human-readable description. |
An issue about the descriptor names no rule: its indices are ValidationIssue.NO_INDEX. The indices follow the policy's normal form, the one build() encodes: groups and rules sorted, constraints on one array and quantifier joined into one loop, a gte and lte pair on one path joined into one between. They do not follow the order of .add() and .or() calls; match an issue to a constraint by its scope and path.
Severity levels
PolicyBuilder.build() reverts on any issue regardless of severity. Use PolicyBuilder.validate() to inspect issues without reverting.
| Severity | Meaning |
|---|---|
| Error | The policy is logically invalid (contradictions, type mismatches). |
| Warning | The policy is valid but contains redundant constraints. |
| Info | The constraint is technically correct but vacuous (always true). |
Issue catalog
Type mismatch
Operators, operands, and paths that do not fit the descriptor's declared types. All are Error severity.
| Code | Description |
|---|---|
INVALID_DESCRIPTOR | Descriptor whose composite node metadata disagrees with its children. |
UNNAVIGABLE_PATH | Path that does not navigate the descriptor. |
QUANTIFIER_ON_NON_ARRAY | Loop path that does not end at an array. |
HINT_MISMATCH | Hint other than the compilation of its path against the descriptor. |
NUMERIC_OP_ON_NON_NUMERIC | Ordering comparison on a type that is neither uint* nor int*. |
BITMASK_ON_INVALID | Bitmask comparison on a bool or function. |
IN_ON_BOOL | isIn/notIn on a bool. |
CONTEXT_TYPE_MISMATCH | Context operand whose property type does not pair with the target type. |
Contradiction
Make the constraint set unsatisfiable. All are Error severity.
| Code | Description |
|---|---|
OUT_OF_PHYSICAL_BOUNDS | Constant outside its type's values. |
CONTRADICTORY_RULE | Rule at which the rules on its subject stop being satisfiable together (PV-9). |
Redundancy
Constraints with no effect because others imply them. All are Warning severity.
| Code | Description |
|---|---|
REDUNDANT_RULE | Rule the other rules on its subject imply (PV-9). |
DOMINATED_BOUND | Numeric bound dominated by a stricter bound. |
DOMINATED_LENGTH_BOUND | Length bound dominated by a stricter bound. |
Vacuity
Constraints that are always true. Info severity.
| Code | Description |
|---|---|
VACUOUS_RULE | Rule that holds for every value and every context (PV-9). |
Compatibility
Loops that cross a normative limit or hold no test of their element. All are Error severity. Normative limits are fixed for each minor version and every conformant enforcer applies the same values, so buildUnsafe() is the only way to emit a policy that crosses one.
| Code | Description |
|---|---|
QUANTIFIER_OVER_STATIC_LIMIT | Loop over a static array declared longer than MAX_QUANTIFIED_ARRAY_LENGTH (PV-7). |
CONTEXT_ONLY_BODY | Loop body that holds no test of the element (PV-8). |