> ## Documentation Index
> Fetch the complete documentation index at: https://docs.canary.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Check messages: inputs and rules

> An input states the values it accepts. These messages concern input types, ranges and business rules.

*Generated from the `rslc` compiler.*

## E-ENUM-NARROW

**A narrowed enum removes members of its parent.**

A narrowed enum is a parent enum minus some members: `MetroTerritory = Territory - { rural }`. The check asks every removed name to be a member of the parent.

A removed name that the parent lacks is usually a misspelling or a member someone renamed. The check catches it before the narrowed enum accepts a value you meant to exclude.

**Example**

`MetroTerritory` removes `coastal`, and `Territory` has only `urban`, `suburban` and `rural`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        rural
    },
    MetroTerritory = Territory - { coastal }
}
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-ENUM-NARROW bad/rater.rsl:22:5: enum narrowing 'MetroTerritory' subtracts member not in parent 'Territory'
```

**Fix**

Remove a member that the parent enum declares.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        rural
    },
    MetroTerritory = Territory - { rural }
}
```

**Other causes**

* The narrowing subtracts a whole type, `Territory - Coastal`, and `Coastal` is an undeclared name. Declare that enum, or list the members in braces.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     Territory = {
>         urban,
>         suburban,
>         rural
>     },
>     MetroTerritory = Territory - { coastal }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     Territory = {
>         urban,
>         suburban,
>         rural
>     },
>     MetroTerritory = Territory - { rural }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-ENUM-BACKING

**A CSV-backed enum needs its member file.**

`Territory = Enum` declares an enum whose members live in a CSV file. The compiler reads `enums/Territory.csv` beside the rater: a folder named `enums` and a file named after the type.

The file has an `ident` column for member names. A `value` column holds the text a request sends, and `label` holds the words a person reads. The check asks for at least one member, each with its own name and its own value.

A long member list, such as territories or occupations, stays in data this way. The check confirms that the list arrived whole before any input or table uses it.

**Example**

The rater declares `Territory = Enum`, and the folder beside it holds only `rater.rsl`.

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-ENUM-BACKING bad/rater.rsl:17:5: enum `Territory` has no members: add `enums/Territory.csv` with columns `ident,value`
```

**Fix**

Keep the rater as it is and add `enums/Territory.csv` beside it, with one row per member. For a short list, another fix is to write the members inline: `Territory = { urban, suburban, rural }`.

```csv enums/Territory.csv theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
ident,value,label
urban,U,Urban
suburban,S,Suburban
rural,R,Rural
```

**Other causes**

* The file has a header row and zero member rows, or lacks both the `ident` and `value` columns. Add the columns and a row for each member.
* Two rows share one `ident`. Give each member its own name.
* Two rows share one `value`. Give each member its own value, so a request names exactly one member.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     Territory = Enum
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```csv good/enums/Territory.csv theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> ident,value,label
> urban,U,Urban
> suburban,S,Suburban
> rural,R,Rural
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     Territory = Enum
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-RANGE

**A range states two bounds in increasing order.**

A range states the values that an input, a calculation or a type accepts: `where self in [0, 100000000]`. The check asks for the lower bound first and the upper bound second. An underscore, `_`, marks an open side.

A square bracket includes its endpoint and a round bracket excludes it. An input accepts the values on its precision grid, so an excluded endpoint closes to the nearest grid value inside the range. At `precision: 0.01`, `(0, 1]` accepts 0.01 to 1.

Each range then has one reading, and the input contract states it. The check catches a range that accepts zero values before a request meets it.

**Example**

The range of `payroll` puts the upper bound first.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
    Policy = {
        inputs {
            payroll: Num {precision: int} where self in [100000000, 0]
        },
        step rating {
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-RANGE bad/rater.rsl:23:13: a range states two positions in increasing order
```

**Fix**

Write the lower bound first.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
    Policy = {
        inputs {
            payroll: Num {precision: int} where self in [0, 100000000]
        },
        step rating {
```

**Other causes**

* A round bracket sits beside `_`, as in `(_, 5]`. Write the open side with a square bracket: `[_, 5]`.
* The range excludes its only value, as in `(5, 5]`. Write `[5, 5]` or widen the range.
* A date range uses a round bracket. Dates are whole days, so write the inclusive days: `[2026-01-02, 2026-12-31]`.
* A date range leaves both sides open. Give it at least one date.
* An input that lacks a precision excludes an endpoint. Declare a precision, or write the smallest accepted value as an inclusive bound.
* An excluded endpoint lies off the input's grid, as in `(0.5, 100]` at `precision: int`. Write the endpoint on the grid.
* The grid has zero values inside the range, as in `(0, 1)` at `precision: int`. Widen the range or refine the precision.
* An `api_default` lies outside the input's range, so a request that omits the input fails. Write a default inside the range.
* A calculation tests `x in [_, _]`. Give the interval at least one finite bound.
* A value states two ranges joined by `and`. State one range; this cause also gets `E-REFINEMENT`.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [100000000, 0]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-DEFAULT

**An omitted input needs an explicit value.**

An input is a fact the request supplies. The check stops `default:` in an input's type, where a value would appear silently when a request omits the input.

Declare an input that a request may omit as `Option(T)`. The calculation then handles both cases with `match`: `Some(c)` for a supplied value and `None` for a missing one.

The value used for a missing answer becomes a visible line of the calculation. A reviewer reads it beside the rest of the arithmetic.

**Example**

`schedule_credit` declares `default: 1` in its type.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        inputs {
            payroll: Num {precision: int} where self in [0, 100000000],
            schedule_credit: Num {precision: 0.01, default: 1} where self in [0.75, 1.25]
        },
        step rating {
            premium: Premium [cite: manual:p3] =
                round(self.payroll / 1000 * rate_per_1000 * self.schedule_credit, 2, half_up)
        }
    }
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-DEFAULT bad/rater.rsl:24:13: attribute `schedule_credit` cannot declare an implicit default; use Option(T) and handle None
```

**Fix**

Declare the input as `Option(Num)` and name the credit that each case gives. Another fix keeps the input a plain number and adds `[api_default: "1"]` before `where`; the rating API then fills in an omitted input.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        inputs {
            payroll: Num {precision: int} where self in [0, 100000000],
            schedule_credit: Option(Num) {precision: 0.01} where self in [0.75, 1.25]
        },
        step rating {
            credit: Num = match self.schedule_credit { Some(c) => c, None => 1 },
            premium: Premium [cite: manual:p3] =
                round(self.payroll / 1000 * rate_per_1000 * self.credit, 2, half_up)
        }
    }
```

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             schedule_credit: Num {precision: 0.01, default: 1} where self in [0.75, 1.25]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] =
>                 round(self.payroll / 1000 * rate_per_1000 * self.schedule_credit, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             schedule_credit: Option(Num) {precision: 0.01} where self in [0.75, 1.25]
>         },
>         step rating {
>             credit: Num = match self.schedule_credit { Some(c) => c, None => 1 },
>             premium: Premium [cite: manual:p3] =
>                 round(self.payroll / 1000 * rate_per_1000 * self.credit, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-INPUT-GENERATE

**The rules admit at least one request.**

The ranges and rules of a rater state which requests it accepts. The check asks that at least one request passes all of them.

A rater that refuses every request usually has a mistyped bound, or two rules that contradict each other. The message names the record, the input and the rules that leave it empty.

The compiler proves this from each input's own range and rules. It also tries every value of an input with a few thousand values against the record rules that read only that input. For rules that combine several inputs, `rslc inputs sample rater.rsl` searches for an accepted request.

**Example**

`employees` accepts 1 to 50, and the rule on `Policy` asks for at least 100.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
            premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
    } where self.employees >= 100
}
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-INPUT-GENERATE bad/rater.rsl:21:5: no request is admitted: Policy field `employees` has no value that meets Policy where self.employees >= 100
```

**Fix**

Correct the bound that is wrong, so that some value passes both. Here the rule asks for 10 employees. Where the range is the mistake, widen the range.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
            premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
    } where self.employees >= 10
}
```

**Other causes**

* One input states two rules that share zero values, such as `where self in [0, 100] where self in {200, 300}`. Change one of them.
* A record rule reads zero inputs and is false, such as `where 1 = 2`. Delete the rule or correct it.
* A list requires at least one element, and the element's record accepts zero values. Correct the element's rules.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             employees: Num {precision: int} where self in [1, 50]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     } where self.employees >= 100
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             employees: Num {precision: int} where self in [1, 50]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     } where self.employees >= 10
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-ENUM-NUMERIC

**An ordered scale of numbers is a number.**

An enum is a set of names, and only a number has order and arithmetic. The check finds an enum whose members are all numerals, such as `"1"`, `"2"` and `"3"`, and whose use depends on their order.

Two uses show that order. A calculation sorts a number into the members with comparisons such as `< 3` and `< 10`. Or an `if` chain groups neighbouring members, such as tiers 1 and 2 together.

In both, the real key is a number and its bands. Declare the bands as a type, such as `IntBands(0, 3, 10..)`, and key the table on the number. The compiler then checks that the bands cover the whole range of the input. An enum whose numeral members are only compared for equality passes the check.

**Example**

`tier` sorts `years_in_business` into the numeral members of `Tier`, and the table is keyed on `Tier`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
Types = {
    Tier = {
        t1 = "1",
        t2 = "2",
        t3 = "3"
    }
}
...
Tables = {
    TierFactor [cite: manual:p4] :
        table dims (tier: Tier) -> {
            factor: Num
        }
...
        },
        step rating {
            tier: Tier =
                if self.years_in_business < 3 then @Tier.t1
                else if self.years_in_business < 10 then @Tier.t2
                else @Tier.t3,
            tier_factor: Num = lookup!(TierFactor, self.tier).factor,
            premium: Premium [cite: manual:p3] =
                round(self.payroll / 1000 * rate_per_1000 * self.tier_factor, 2, half_up)
```

```csv theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
tier,factor
1,1.25
2,1.10
3,1.00
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-ENUM-NUMERIC bad/rater.rsl:43:17: enum Tier members "1".."3" are numbers, and `tier` classifies a number into its members with ordered comparisons. Use a `Num {precision: int}` input with range [1, 3] keyed by a IntValues or band dimension
  = because: a number is a number: an enum of numerals hides order and arithmetic, and its bands are a dimension
  = help: declare the input as `Num {precision: int} where self in [1, 3]`, declare `TierValues = IntValues(…)` or a band type, key the tables on it, and delete enum `Tier`
  = applicability: manual
```

**Fix**

Delete the enum and the calculation that fills it. Declare the bands as `YearsInBusiness = IntBands(0, 3, 10..)`, key the table on that type, and look up the years directly. Each CSV row starts at its band's lower bound.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
Types = {
    YearsInBusiness = IntBands(0, 3, 10..)
}
...
Tables = {
    TierFactor [cite: manual:p4] :
        table dims (years: YearsInBusiness) -> {
            factor: Num
        }
...
        },
        step rating {
            tier_factor: Num = lookup!(TierFactor, self.years_in_business).factor,
            premium: Premium [cite: manual:p3] =
                round(self.payroll / 1000 * rate_per_1000 * self.tier_factor, 2, half_up)
```

```csv theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
years,factor
0,1.25
3,1.10
10,1.00
```

**Other causes**

* An input has the numeral enum as its type, and an `if` chain groups neighbouring members into bands. Declare the input as a number with a range, and key a table on a band type.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     Tier = {
>         t1 = "1",
>         t2 = "2",
>         t3 = "3"
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Tables = {
>     TierFactor [cite: manual:p4] :
>         table dims (tier: Tier) -> {
>             factor: Num
>         }
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             years_in_business: Num {precision: int} where self in [0, 100]
>         },
>         step rating {
>             tier: Tier =
>                 if self.years_in_business < 3 then @Tier.t1
>                 else if self.years_in_business < 10 then @Tier.t2
>                 else @Tier.t3,
>             tier_factor: Num = lookup!(TierFactor, self.tier).factor,
>             premium: Premium [cite: manual:p3] =
>                 round(self.payroll / 1000 * rate_per_1000 * self.tier_factor, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```csv bad/tables/TierFactor.csv theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> tier,factor
> 1,1.25
> 2,1.10
> 3,1.00
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     YearsInBusiness = IntBands(0, 3, 10..)
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Tables = {
>     TierFactor [cite: manual:p4] :
>         table dims (years: YearsInBusiness) -> {
>             factor: Num
>         }
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             years_in_business: Num {precision: int} where self in [0, 100]
>         },
>         step rating {
>             tier_factor: Num = lookup!(TierFactor, self.years_in_business).factor,
>             premium: Premium [cite: manual:p3] =
>                 round(self.payroll / 1000 * rate_per_1000 * self.tier_factor, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```csv good/tables/TierFactor.csv theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> years,factor
> 0,1.25
> 3,1.10
> 10,1.00
> ```

## E-CODE-PATTERN

**A code value matches its declared pattern.**

A `Code` type holds identifiers such as class codes and ZIP codes. Its `pattern` states the exact shape of a valid code: `Code(pattern: "[0-9]{4}")` accepts four digits.

The check asks every code written in the rater to match the whole pattern. It also asks the pattern itself to be one the compiler reads.

The same pattern refuses a request with a malformed code before rating starts. A literal that fails the pattern would compare equal to zero accepted codes, so its branch would be dead. The check finds that typing slip in the rater.

**Example**

`ClassCode` accepts four digits, and the comparison writes the five-digit code `"70010"`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        },
        step rating {
            class_factor: Num = if self.class_code = "70010" then clerical_factor else 1,
            premium: Premium [cite: manual:p3] =
                round(self.payroll / 1000 * rate_per_1000 * self.class_factor, 2, half_up)
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-CODE-PATTERN bad/rater.rsl:32:54: code literal "70010" does not fully match `ClassCode` pattern "[0-9]{4}"
```

**Fix**

Write a code that matches the pattern. Where the filing's codes have five digits, change the pattern to `"[0-9]{5}"`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        },
        step rating {
            class_factor: Num = if self.class_code = "7001" then clerical_factor else 1,
            premium: Premium [cite: manual:p3] =
                round(self.payroll / 1000 * rate_per_1000 * self.class_factor, 2, half_up)
```

**Other causes**

* The type is `Code()` and lacks a pattern. Add one: `Code(pattern: "[0-9]{4}")`.
* The pattern is a number or a name. Write it as a string in double quotes.
* The pattern accepts `_` or `..`, which table cells reserve for wildcards and open bands. Tighten the pattern to exclude them.
* The pattern has a syntax error, such as an unclosed `[`, or uses `.` for any character. List the allowed characters in a class such as `[0-9A-Z]`.

A table key cell that fails the pattern gets `E-TABLE-COL`, and that message names `E-CODE-PATTERN` in its text.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     ClassCode = Code(pattern: "[0-9]{4}")
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2,
>     clerical_factor [cite: manual:p4] = 0.5
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             class_code: ClassCode
>         },
>         step rating {
>             class_factor: Num = if self.class_code = "70010" then clerical_factor else 1,
>             premium: Premium [cite: manual:p3] =
>                 round(self.payroll / 1000 * rate_per_1000 * self.class_factor, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Types = {
>     ClassCode = Code(pattern: "[0-9]{4}")
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2,
>     clerical_factor [cite: manual:p4] = 0.5
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             class_code: ClassCode
>         },
>         step rating {
>             class_factor: Num = if self.class_code = "7001" then clerical_factor else 1,
>             premium: Premium [cite: manual:p3] =
>                 round(self.payroll / 1000 * rate_per_1000 * self.class_factor, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-INPUT-DOMAIN

**A number input states its precision and range.**

A number input declares two things. Its precision is the grid of values a request may send: `precision: int` for whole numbers, or a decimal step such as `precision: 0.01`. Its range is the lowest and highest value the filing accepts: `where self in [0, 100000000]`.

The check asks every number input for both. With them, the rating API refuses a payroll of -5 or 12.345 before any arithmetic runs. The compiler also uses the range to prove that each lookup finds a row and each division has a nonzero divisor.

**Example**

`payroll` declares a precision and omits the range.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
    Policy = {
        inputs {
            payroll: Num {precision: int}
        },
        step rating {
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-INPUT-DOMAIN bad/rater.rsl:23:13: numeric input `Policy.payroll` declares no domain: add a range `where self in [min, max]` with the bounds the filing states. One side may be `_`; write `[_, _]` only for a signed value that the filing bounds on neither side. Every numeric input states the values the filing and the rating API accept, for example `payroll: Num {precision: 0.01} where self in [0, _]`
cost premium: 1 (each list of size 1: 1, 3: 1, 10: 1)
```

**Fix**

Add the range that the filing states. Write `_` for a side that the filing leaves open, as in `[0, _]`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
    Policy = {
        inputs {
            payroll: Num {precision: int} where self in [0, 100000000]
        },
        step rating {
```

**Other causes**

* The input states a range and omits the precision, as in `payroll: Num where self in [0, 100000000]`. Add `{precision: int}` or a decimal step.
* The input declares `precision: factor`, which belongs to calculated values. Give the input a decimal step such as `precision: 0.01`.
* The input declares `precision: exact` and omits the range. Add the range; reserve `exact` for a value whose scale the filing leaves unstated.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int}
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-REFINEMENT

**A rule takes the form its position accepts.**

A `where` rule follows an input, a list, a record or a calculation, and each position accepts its own forms. The check finds a rule in a form that its position leaves out.

| Position | Forms |
| - | - |
| A number or date input | A range `self in [a, b]`, a set `self in {a, b}`, a condition over `self` |
| A list | `len(self) in [a, b]`, `unique(self as x, x.key)`, `all`, `exists`, `count` |
| A record | A condition over the record's inputs and its child records |
| A calculation | One range, `self in [a, b]` |

A rule on an input, a list or a record is a test: the rating API refuses a request that fails it. A range on a calculation is a claim, and the compiler proves it from the input ranges. A comparison or a condition over inputs is a test, so it belongs on an input or a record.

**Example**

The calculation `premium` states its lower bound as the comparison `self >= 0`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        },
        step rating {
            premium: Premium [cite: manual:p3] where self >= 0 =
                round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-REFINEMENT bad/rater.rsl:26:54: `premium` is a calculation: its refinement is a proof obligation, `where self in [a, b]`; a predicate over inputs is an input refinement
```

**Fix**

Write the bound as a range. The compiler then proves that the premium stays at or above zero for every accepted request.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        },
        step rating {
            premium: Premium [cite: manual:p3] where self in [0, _] =
                round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
```

**Other causes**

* A calculation's rule tests an input, such as `where self.payroll >= 1000`. Move the rule after the record.
* A calculation's type is an alias whose rule has a code or a condition. Use a type whose only rule is a range.
* A list length has fractional bounds or puts the larger bound first, as in `len(self) in [10, 1]`. Write whole numbers in increasing order.
* `unique` has another shape than `unique(self as x, x.key)`. Bind the element with `as`, and name one required input of the element.
* `unique` sits in a record rule. Write it after the list input: `locations: List(Location) where unique(self as l, l.address)`.
* A list rule follows an `Option(List(…))` input. Declare a plain `List(…)`, where an empty list stands for a missing one.
* A rule follows an input that holds one child record. Write the rule after that record's type.
* One input states two ranges or two sets. Merge them into one.
* An input carries `[child]`, which RSL 0.4.1 retired. Declare `name: Location` for one record or `name: Option(Location)` for an optional one.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] where self >= 0 =
>                 round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] where self in [0, _] =
>                 round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-ANNOTATION

**A refusal code follows its rule, in capitals.**

A rule that refuses a request names the refusal after its condition: `[code: "…", message: "…", cite: source:region]`. The rating API returns the code and the message to the caller. The citation points to the filed rule.

The check asks for three things. The annotation follows a `where` rule on an input, a list or a record. It holds only `code`, `message` and `cite`. The code starts with a capital letter and continues with capitals, digits, `_` or `-`.

Other systems match on the code, so one fixed spelling keeps it stable. Annotations that describe the declaration itself, such as `label`, sit before `where`.

**Example**

The code `payroll-range` is in lower case.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        inputs {
            payroll: Num {precision: int}
                where self in [0, 100000000] [code: "payroll-range", message: "Payroll must be 0 to 100,000,000."]
        },
        step rating {
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-ANNOTATION bad/rater.rsl:24:47: refinement code `payroll-range` must match `[A-Z][A-Z0-9_-]*`, like a `reject` code
```

**Fix**

Write the code in capitals.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        inputs {
            payroll: Num {precision: int}
                where self in [0, 100000000] [code: "PAYROLL-RANGE", message: "Payroll must be 0 to 100,000,000."]
        },
        step rating {
```

**Other causes**

* The message is an empty string. Write the sentence the caller reads.
* `code` or `message` has a number as its value, or stands alone as `[code]`. Give it a string in double quotes.
* Another annotation follows the rule, such as `[label: "…"]` or `[required]`. Move it to the declaration, before `where`.
* `code` and `message` sit on the input, before `where`. Move them after the rule they name.
* An input carries `code` and `message` and lacks a rule for them to name. Add a `where` rule or remove them.
* A calculation or a constant carries `code` or `message`. Remove them; a calculation's range is a claim the compiler proves, so it refuses zero requests.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int}
>                 where self in [0, 100000000] [code: "payroll-range", message: "Payroll must be 0 to 100,000,000."]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int}
>                 where self in [0, 100000000] [code: "PAYROLL-RANGE", message: "Payroll must be 0 to 100,000,000."]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-REFINE-CANONICAL

**A bound on an input is a range.**

A rule on an input that compares it with one number is a bound. The check asks for every bound in one spelling, the range: `self in [0, _]` in place of `self >= 0`.

With one spelling, the compiler reads the range of every input directly. It uses that range in the input contract, in its proofs about lookups and division, and when it generates sample requests. A reader of the rater finds each input's limits in the same place and form.

A square bracket includes the endpoint, a round bracket excludes it, and `_` leaves a side open. The message prints the range to write.

The check covers the rules written on one input. A rule written after a record, such as `where self.payroll >= 1000`, keeps its comparison.

**Example**

`payroll` states its lower bound as the comparison `self >= 0`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
    Policy = {
        inputs {
            payroll: Num {precision: int} where self >= 0
        },
        step rating {
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-REFINE-CANONICAL bad/rater.rsl:23:49: a bound is written as one membership: `self in [0, _]`, not `self >= 0` (E-REFINE-CANONICAL)
```

**Fix**

Write the bound as the range the message prints. Add the upper bound to the same range where the filing states one, as in `self in [0, 100000000]`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
    Policy = {
        inputs {
            payroll: Num {precision: int} where self in [0, _]
        },
        step rating {
```

**Other causes**

* A date input compares `self` with one date. Write the range of dates: `self in [2026-01-01, _]`.
* A list bounds its length with a comparison, such as `len(self) <= 5`. Write `len(self) in [_, 5]`.
* An input fixes one value with `(self = 5)`. Write the set, `self in {5}`, in a second `where` after the range.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self >= 0
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, _]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-REFINE-UPWARD

**A rule reads its own record and below.**

A `where` rule decides whether the rating API accepts a request. The rule is part of the input contract, so it reads inputs: the value it follows and the records beneath that value.

The check finds a rule that reads further: a parent record, a neighbouring input, a calculation or a rating outcome. In the example, a rule after `Location` reads `Policy`, which sits above it.

Write a rule that relates a child to its parent on the parent, over its children. The parent reaches both sides: its own inputs through `self`, and each child through `all`.

**Example**

The rule after `Location` compares a location's payroll with `Policy.payroll`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
            premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
    },
    Location = {
        inputs {
            payroll: Num {precision: int} where self in [0, 100000000]
        }
    } where self.payroll <= Policy.payroll
}
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-REFINE-UPWARD bad/rater.rsl:34:7: a refinement reads the refined value and its descendants, never an ancestor, a sibling or a calculation; write a rule that relates a record to its ancestor on the ancestor, over its children: `Policy = { … } where all(self.drivers as d, d.x <= self.y)`
```

**Fix**

Move the rule to `Policy` and state it over the locations with `all`.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
            premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
    } where all(self.locations as l, l.payroll <= self.payroll),
    Location = {
        inputs {
            payroll: Num {precision: int} where self in [0, 100000000]
        }
    }
}
```

**Other causes**

* A record rule reads a calculation over the record's own inputs, such as `self.payroll_thousands >= 1`. Write the formula in the rule: `self.payroll / 1000 >= 1`.
* A record rule reads a rating outcome, such as `self.premium >= 100`. State the rule over the inputs that decide it.
* A rule on an input or a list reads a calculation, such as `all(self as l, l.half <= 100)`. State the rule over inputs: `all(self as l, l.payroll / 2 <= 100)`.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             locations: List(Location) where len(self) in [1, 10]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     },
>     Location = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         }
>     } where self.payroll <= Policy.payroll
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000],
>             locations: List(Location) where len(self) in [1, 10]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     } where all(self.locations as l, l.payroll <= self.payroll),
>     Location = {
>         inputs {
>             payroll: Num {precision: int} where self in [0, 100000000]
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```

## E-DERIVABLE-INPUT

**Calculate a value that other inputs fix.**

An input is a fact that only the caller knows. The check finds an input that a rule forces to equal a formula of other inputs, so the caller has exactly one acceptable answer.

Asking for such a value adds a question and a way for the request to disagree with itself. Calculate the value in the rater. The request gets shorter, and the total always matches its parts.

A rule that sets two independent facts equal passes the check. An example is a filing that requires two coverage limits to match, where each limit also feeds the rating.

**Example**

The rule after `Policy` forces `payroll` to equal the sum of the other two inputs.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        inputs {
            clerical_payroll: Num {precision: int} where self in [0, 50000000],
            field_payroll: Num {precision: int} where self in [0, 50000000],
            payroll: Num {precision: int} where self in [0, 100000000]
        },
        step rating {
            premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
    } where self.payroll = self.clerical_payroll + self.field_payroll
}
```

```sh theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
rslc check bad/rater.rsl
```

```text theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
FAIL bad/rater.rsl (strict)
error: E-DERIVABLE-INPUT bad/rater.rsl:25:13: input `Policy.payroll` is fixed by a refinement to equal a value computed from `Policy.clerical_payroll`, `Policy.field_payroll`, so the caller can only ever send that value. Delete the input and compute it
  = because: an input that must equal a function of other inputs is not a fact the caller supplies; asking for it twice only adds a way to disagree
  = help: delete input `payroll` and its refinement equality, and declare the computed attribute `payroll: <T> = <the right-hand side>` in its place
  = applicability: placeholders
```

**Fix**

Delete the input and its rule, and declare `payroll` as a calculation in a step.

```rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
        inputs {
            clerical_payroll: Num {precision: int} where self in [0, 50000000],
            field_payroll: Num {precision: int} where self in [0, 50000000]
        },
        step rating {
            payroll: Num = self.clerical_payroll + self.field_payroll,
            premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
        }
    }
}
```

**Other causes**

* An input appears only in a rule that refuses a mismatch, such as `where not (self.declared_payroll != self.clerical_payroll + self.field_payroll)`. Delete the input and the rule, and read the formula where the value is needed.
* A presence flag guards that comparison, as in `where not (self.has_declared_payroll and …)`. Delete the flag as well.

For a rule between two independent facts, write the rule after the record's closing brace: `} where self.clerical_payroll = self.field_payroll`.

> [accordion] Complete files
>
> ```rsl bad/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             clerical_payroll: Num {precision: int} where self in [0, 50000000],
>             field_payroll: Num {precision: int} where self in [0, 50000000],
>             payroll: Num {precision: int} where self in [0, 100000000]
>         },
>         step rating {
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     } where self.payroll = self.clerical_payroll + self.field_payroll
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```
>
> ```rsl good/rater.rsl theme={"languages":{"custom":["languages/rsl.tmLanguage.json"]}}
> spec {
>     rsl: "0.4.1",
>     carrier: "Example Mutual",
>     state: AZ,
>     product: "Staffing Liability",
>     effective_date: 2026-01-01
> }
>
> Sources = {
>     manual: Source = {
>         artifact: artifact("00000000-0000-4000-8000-000000000000"),
>         kind: pdf
>     }
> }
>
> Constants = {
>     rate_per_1000 [cite: manual:p3] = 2
> }
>
> Records = {
>     Policy = {
>         inputs {
>             clerical_payroll: Num {precision: int} where self in [0, 50000000],
>             field_payroll: Num {precision: int} where self in [0, 50000000]
>         },
>         step rating {
>             payroll: Num = self.clerical_payroll + self.field_payroll,
>             premium: Premium [cite: manual:p3] = round(self.payroll / 1000 * rate_per_1000, 2, half_up)
>         }
>     }
> }
>
> RatingOutcomes = {
>     premium: Policy.premium
> }
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.