Skip to main content
Schemas say what data looks like. Rules say whether it can be trusted. A rule is a named check with a description, a function that returns true or a failure message, and an optional list of fields it touches. Rules run after schema validation; if any rule fails, Boundary turns the failure into a repair message and asks the model to try again.

The Rule type

A canonical rule

A few things this snippet bakes in:
  • The name is snake_case and reads like a machine key. The dashboard joins on it.
  • The description is a positive statement of the invariant, not the error text.
  • The check short-circuits the not-applicable case (lead.tier !== "hot"), then asserts the invariant (lead.score >= 70). When neither holds, it returns a dynamic failure string — context like the actual score and the threshold makes the model’s repair faster.
  • fields identifies the output fields the rule reads. Boundary can infer simple cases, but explicit fields make docs, logs, and dashboard filters easier to read.

What check may return

Returning a tailored string is almost always better than returning false with a static message — the closer the failure text is to “here’s what’s wrong, here’s what to do,” the better the repair on the next attempt.

Sync, deterministic, cheap

check is not async. Rules run inline during validation. If you need an async check (database lookup, external API), do it before or after the contract — never inside.
Same input, same output. No randomness, no clocks, no external state. Determinism is what makes rules unit-testable and what lets the dashboard report stable per-rule failure counts.
Rules run on every attempt — up to maxAttempts times per contract call. Keep them in-memory. No DB queries, no fetch, no heavy parsing.

Domain examples

Lead scoring

Finance

Support ops

Agents

Compliance

String messages drive repair

The string a failed rule returns becomes part of the repair prompt sent to the model. Specific, contextual messages produce specific, contextual repairs.
The model sees: “invalid”. Not enough to fix anything.

Multiple rules

Rules are evaluated in order. All failing rules are collected — Boundary does not short-circuit on the first failure. Every violation goes back to the model in a single repair message.

Validation at construction time

defineContract validates rules when you build the contract — not lazily on the first run. You catch typos, duplicate names, and over-long descriptions at startup, not in production:
  • name is required, ≤128 chars, unique within the contract.
  • description is ≤1000 chars when provided.
  • fields is ≤64 entries, each ≤128 chars when provided.
  • check must be a function.
Anything that fails these checks throws a TypeError at defineContract time. Test it once with pnpm tsc --noEmit or your test runner; it’ll never bite you in prod.

Next steps

The Repair Loop

How violations become targeted fixes

Results

What you get back from a contract call

Observability

Named rules are useful locally and in production. Use local development logging to see exact rule failures while building, then add production observability to track top failing rules across traffic.