Compiled rules
A rule file can say in words what its operations may do, and a model can compile that text into rules. This page covers rules written as intent, turning compilation on and the companion file a compile writes. Guardrails covers the rule files themselves, the checkpoints, and the edits that widen what a file allows. Checks never call a model: a compile writes rules, and every check runs rules in your process.
Rules written as intent
A rule file's intent says in words what its operations may do. It needs a target, since rules written as intent
compile only for a set bound to a target, and compile says when the text is compiled into rules: target-change,
when the target's schema changes, and intent-change, when the text changes.
intentsharp: rules/1
set: orders-data
checkpoint: data
target: sqlite
intent: >
Answer questions about customers by reading the Customers table. Never read the PaymentCards table.
Never change data.
compile: [target-change, intent-change]
rules:
- id: no-cards
subject: data.entities
deny: [PaymentCards]
message: Payment cards are not available here.
- id: read-only
subject: data.operations
allow: [read]
message: Only read; this application never changes data through the model.
A file with intent and no target does not load, and its last good version stays in force.
Change detection covers what happens while a set waits for a compile that
failed, reached its cap or was held.
Turn compilation on
Compilation is off until you turn it on: WithRuleCompilation(store) after WithGuardrailFiles on the builder, or
services.AddRuleCompilation() beside AddGuardrails. WithGuardrailFiles and AddGuardrails alone compile
nothing. Both calls are experimental, and a call is a compiler error, INTENTSHARP001, until you suppress it:
#pragma warning disable INTENTSHARP001 // rule compilation is experimental
using var client = new IntentClientBuilder()
.UseProviderOptions(store)
.WithDocumentStore(store)
.WithGuardrailFiles(store, "requests")
.WithRuleCompilation(store) // without this call, no rule written as intent is compiled
.Build();
#pragma warning restore INTENTSHARP001
On a client that does not compile, a file with compile loads with its own rules alone: this one denies
PaymentCards and allows no entity, so it refuses every command until a companion allows some. Under dependency
injection the rule file source logs a warning once for each version of such a file; a client built with the builder
does not log it.
What a compile writes
A compile asks the provider of the maintenance role, by default the first provider of answer
(Provider file), and its calls are checked at the Request checkpoint like any
other. It writes the rules into a companion beside the rule file, orders.rules.compiled.json, as an edit of the
client's store. The companion is checked before it runs, runs after the file's own rules, and carries no settings, so
it cannot turn writes on; a deny in either wins.
The intent that compiles the text is a document in the store too. A developer or a model may replace its running version, and an edit of it is never flagged as widening; the checks below stay in the library's code whatever that document says.
What a compile refuses
- On
data.entitiesthe compile refuses a name the target does not describe. It does not refuse"*"or"@described": a companion that holds one is applied, and flagged as widening. A writer of your own can hold such an edit before it runs (Guardrails). - A pattern in a compiled rule may not use lookarounds, backreferences, atomic groups or conditionals: the compile refuses one and tells the model why, and a companion that holds one does not load. A pattern you write in a rule file has no such limit.