WHERE is indistinguishable from a deliberate one — after the fact. Warden decides before, from the structure of the operation:
- Bounded — the predicate provably restricts it to an enumerable set of records.
- Unbounded — it does not, and the reason is recorded: no predicate at all, a comparison that cannot be normalised, a write derived from another resource with nothing bounding it.
unbounded_mutation as its own class, because “change every record” deserves a different answer from “change this one”.
What counts as a bound
Equality on an identifying field, and membership in a list of them, bound an operation. Ranges, pattern matches, negations, and existence checks do not:WHERE created_at > '2020-01-01' matches an unknown number of records, and calling that bounded would be a lie of exactly the kind this analysis exists to prevent.
An operator the connector cannot normalise makes the operation unbounded and raises a hazard — the honest representation of “this is well-formed, and I cannot reason about it”.
Scope is not a bound
Scope limits whose records an operation reaches. Bounds limit how many. Conflating them is how an approval queue starts showing “1 tenant affected” for a statement that empties a table, and it is why scope never contributes to the bound the reviewer is shown. The certification suite checks this specifically: applying scope must never change the blast radius.Hazards
Alongside the bound, an operation carries hazards — properties a reviewer or a policy should react to:
Policy denies on hazards; the reviewer sees them on the pending card; audit records them. The same statement is described the same way in all three places.