How to Let AI Write Business Rules Without Letting It Touch Production

Every team that keeps business rules outside its code has a backlog of rule changes. "Hold orders over $10,000 from new customers." "Give Gold members free shipping on weekends." Each one is small, specific and urgent, and the people who know what the rule should say usually aren't the people who can write it.

LLMs look like the obvious fix. Describe the rule in a sentence and let the model write it. The problem is what happens next. A rule that decides discounts, shipping holds or loan approvals is production logic. An AI that writes it straight into production is a deployment pipeline with no tests, no review and no rollback, driven by something that's confidently wrong a few percent of the time.

Code Effects 7 was designed to give teams both: let AI agents do real rule-authoring work, and make it structurally impossible for them to change what runs in production on their own. Here's how it fits together.

The setup: rules live in your app

Code Effects is embedded. The rule editor, the engine and, in v7, the Rule Repository (storage, versioning, approvals, tests) all run inside your own ASP.NET application, against your own database. Nothing calls home.

Rules are written against a source object: an ordinary C# class whose properties and methods are what a rule can read, call and set.

public class Order
{
    [Field(DisplayName = "Order total", Min = 0,
           AiAliases = new[] { "amount", "order value" },
           AiDescription = "Order total in USD, after discounts, before tax")]
    public decimal Total { get; set; }

    [Field(DisplayName = "Customer tier")]
    public Tier CustomerTier { get; set; }   // New, Silver, Gold


    [Field(DisplayName = "On hold")]
    public bool OnHold { get; set; }

    [Action(DisplayName = "Notify the fraud team")]
    public void NotifyFraudTeam() { /* your code */ }
}

The attributes are labels for the people and agents writing rules. AiAliases and AiDescription exist only for the AI: they tell an agent that "amount" means Total, and in which currency. The rule editor ignores them.

Giving agents access: an MCP server inside your app

v7 adds an embedded MCP server. It's one endpoint in your application, behind your authentication:

builder.Services
    .AddCodeEffectsMcp(options =>
    {
        options.MapCaller = (user, http) => Callers.Map(user);
        options.Instructions = "These rules decide order discounts and shipping holds.";
    })
    .WithRepository(repository => repository.DbProvider = dbProvider)
    .WithAudit(audit => audit.DbProvider = dbProvider);

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapCodeEffectsMcp("/mcp").RequireAuthorization();

MapCaller is your code. It turns the signed-in user into a caller with an identity and a set of permissions. No MCP tool takes a credential, and the server never signs anyone in. Your users add https://your-app/mcp to the AI client they already use, and the agent works as them, with their permissions, recorded under their name and the agent's.

From there, an agent has about forty tools: finding fields and methods, reading rules and their history, validating, saving, testing, and asking why a past decision came out the way it did.

Guardrail 1: every rule is validated against your real model

The most common way an LLM gets business logic wrong isn't bad reasoning. It makes things up: a field that doesn't exist, a property name that's close but not quite right, a method called with the wrong arguments, a string compared with a number.

So agents don't write free text, and they don't write the storage XML either. They write rules in a small structured format, and rule_validate checks every one against the source object's current definition. That's the same server-side validation the human editor's rules go through. The answer is precise: complete, unfinished, or wrong, with each problem at its exact place in the rule.

member_find is how the agent learns what exists. It searches your fields, methods, actions and enumerations by words, aliases included, and returns their real types, bounds and allowed operators. An agent that asks for "the order amount" gets Total, a decimal that can't be negative, and the operators that make sense for it.

A typical exchange, condensed:

  1. The user asks: "Hold orders over $10,000 from new customers, and tell the fraud team."
  2. The agent calls member_find for "order amount", "customer tier", "hold" and "fraud team".
  3. It drafts the rule and calls rule_validate. The first attempt compares Customer tier with the text "new customer", which isn't one of the Tier values, and validation points at that value.
  4. It uses Tier.New instead and validates again: the rule is complete.
  5. It shows the user the rule in plain words, roughly: if Order total is greater than 10000 and Customer tier is New, then set On hold to true and notify the fraud team.

The model is good at turning intent into structure. The validator is good at catching what the model got wrong. Neither has to do the other's job.

Guardrail 2: an agent can propose, never publish

This is the guardrail that matters most, and it's deliberately not configurable.

Everything an agent saves is a new version that requests approval. Saving never replaces the live rule. Rule versions are immutable, each rule has at most one live version, and making a version live is a separate step that only a person can take.

Whatever permissions an agent has been given, no agent can:

  • make a version live, or take one offline;
  • approve or decline a version;
  • delete anything;
  • read the team's internal notes.

When the agent wants to change an existing rule rather than create a new one, the save asks the person first. When it saves, your application's notification hook is told, so you can let approvers know there's something to review. And every action lands in the activity log with both names: the person the agent works for and the agent that did it.

In practice the AI becomes a fast, tireless junior author. It drafts, revises, explains and tests. The decision to put something into production stays with a human, every time.

Guardrail 3: test on your real code, before anyone approves

Approving a rule you can't see run is just a rubber stamp. So agents can test, and the tests run on your actual classes:

  • rule_try runs a rule on plain values and returns the verdict, the conditions that decided it and what it would set. It records nothing.
  • test_create and test_run save tests with expected results, so the reviewer sees what the agent checked and whether it passed.
  • change_impact, with the Audit library, replays recent production decisions through the proposed rule and reports how many would have come out differently. "This change would have held 37 of last week's 4,200 orders" is a better basis for approval than reading conditions.

There's one sharp edge: rules can call your own methods, and some of them do real things, like sending an email or calling an API. A test that would make such a call stops and asks the person first. Clients that support MCP's elicitation ask inside the conversation. Others get a confirmation the agent must take back to the person. Either way, an agent can't make a production call from a test without a yes from a human. Agent test runs are also rate-limited by settings you control.

Guardrail 4: the record

Nothing an agent does goes unrecorded. Every save, test and status change is logged with who did it and through which agent. With Audit, every production evaluation records the rule version that ran and the values it read. So when someone asks "why was this order held?", explain_outcome and evaluation_explain can answer from what actually happened, not from what the rule says today.

What it doesn't do

A few honest limits:

  • It's not an autonomous rules engineer. It's built for a person directing an agent, with a person approving the result.
  • Over MCP, your users bring their own AI client. The Rule Repository also has a built-in AI assistant that works with any model supporting tool calling through .NET's IChatClient; it has been tested with Claude and GPT. Results depend on the model, and a cheaper model makes more round trips to the validator.
  • Validation proves a rule is well-formed against your model. It doesn't prove the rule is the policy you meant. That's what tests and the approval step are for.

Try it

The rule editor and engine are free for personal, community, development and research use, as the CodeEffects.Rule.Editor.Personal and CodeEffects.Rule.Engine.Personal packages on NuGet. The Rule Repository, Audit and the MCP server are part of the commercial packages. The docs cover all of it:

How do other teams handle this? If you, as developers, let AI anywhere near business logic, where do you put the human? Share your approach in the comments.

Date: November 13, 2026

Ask on Stack Overflow - Yes, it's still active
Ask in GitHub Discussions

p102

l097 --

l102

p101

×