Where this chapter sits
The first four chapters looked at the organization: where AI work is defined, who may act, what it costs and where the time goes. This chapter goes inside a single program. It is written for the architects and senior developers who build the .NET applications those organizations run on, and it asks the series’ four questions at the smallest scale there is: what a model call means, what an acceptable answer requires, who may change the rules, and what happened when the call ran.
The problem, as you would put it
Every .NET team that has put a model into a business application remembers the first afternoon. You pick a provider, send a prompt with some text, and get back something that looks like what you asked for. The demo works. Someone asks for it in production.
Then the ordinary failures arrive. The model returns a category that is not in your enum. It writes a date your parser rejects. It wraps the JSON in a friendly sentence. It sets a priority of “urgent-ish”. On Tuesday the same input gives a different answer from Monday’s, and nobody can say which one reached the database.
None of this is exotic. It is what happens when a component with a probabilistic output sits inside a pipeline that expects exact types. The call itself was never the hard part. Knowing whether the answer can be trusted, and what to do when it cannot, is the hard part.
There is a second, slower problem. The wording of the prompt, the list of allowed categories and the rules about what
counts as a valid answer end up as string constants and if statements in compiled code. When the business changes a
rule, the change waits for a release. When an auditor asks which rule was in force last month, the answer is somewhere
in the commit history, if anyone can find it.
Why the usual fixes do not hold
The first fix most teams reach for is a better prompt. It helps, until the next unusual input. A prompt states what you want. It does not check what you got.
The second fix is a hand-written loop: parse the answer, validate it, and if it fails, ask again. This works, and almost every team writes one. The trouble is that each team writes its own, often several times inside one application. Each copy has its own idea of how many attempts to allow, what to tell the model on the retry, and what to log. Most of them retry with the same prompt, so the model has no idea what it got wrong. Most of them keep only the final answer, so the failed attempts that would explain a bad result are gone.
The third fix is to move the rules into configuration. That is the right instinct, but a loose settings file has no shape, no version and no check on load. A typo in a list of allowed values becomes a production incident rather than a refused edit.
The fourth fix is to keep the model away from anything that matters: it drafts, and a person retypes the result into the real system. That is cautious, and it gives back most of what the model was meant to save.
Each of these addresses one symptom. None of them gives the call a contract: a stated goal, a stated shape, stated rules, a bounded way to recover, and a record of what happened.
The idea: keep the meaning outside the compiled code
Our thesis starts from a plain observation. Much of business software exists to reconcile differences in representation, and developers keep rewriting code because the business changed its vocabulary, not its need. Its proposal is a separation: a stable execution core carries out exact operations, and external definitions describe the contracts, interpretation rules and domain decisions that change.
That separation applies directly to a model call. The goal of the call, the shape of the answer and the rules an acceptable answer must meet are definitions. They belong in files that can be read, versioned and changed on purpose. The code that sends the call, checks the answer, retries and records the attempts is machinery, and it should be the same for every call in the application.
Put together, a call becomes a checked call (Figure 1). The application states the intent and passes the input. The model’s answer is checked against the .NET type the team owns and against the rules beside the intent. When a rule fails, its message goes back to the model on the next attempt, so the retry carries the finding instead of repeating the question. A transient error at the provider can move the call to the next provider in a configured chain. All of it happens inside limits the team set: attempts, timeouts and model-call ceilings. Every attempt is kept, the failed ones included.
Two properties of that picture matter most. The record is complete: a developer can see what the model returned each time and which rule refused it. And a pass means the checks passed. It does not mean the answer is right.
The thesis draws the boundary that keeps this honest. Arithmetic, transaction rules and permission checks keep exact implementations, and the model is reserved for the part of a case that is genuinely uncertain: “When a case genuinely requires interpretation, a bounded model call can address that uncertainty.” (C2Consultants, Intelligent Software and Autonomous Organizations.) A checked call is that sentence written as machinery: bounded by a type, rules and limits, and used only where interpretation is the job.
What it looks like in a firm’s application
Take a mid-sized firm with an internal application that receives customer requests by email and turns each one into a ticket. Today a person reads every message, picks a category, sets a priority and copies the account details into the ticket form. The team wants a model to do the first pass.
The ticket stays an ordinary .NET type the team owns, with its category, priority and account fields. The intent lives in a file: a sentence saying what the call is for, a few examples, the output requirements, and a description written for people rather than for the model. The rules sit beside it: the priority must be one of the allowed values, the account number must match a pattern, a numeric field must fall within a range.
Six months later the business adds a category. Someone edits the intent file and saves it, and the next call uses the new wording with no release. If the edit does not load, the last good version stays in force while the application runs, and the problem is reported by file and line. Every file carries a version, so the team can activate a version and revert to an earlier one.
The thesis is careful about what this does not mean. “Moving a rule outside a compiled method does not remove the responsibility to define it correctly.” (C2Consultants, Intelligent Software and Autonomous Organizations.) A rule in a file still has to be right. What the file adds is a place where the rule can be inspected, revised and tied to the period in which it was in force.
The approach has edges, and they matter. A new .NET result type, a new named code check, or code that calls a new intent by name still needs a release (Figure 2). And the rules catch the failures the team thought to write down. A person still owns the ones nobody anticipated, which is why the failed attempts are kept where a person can read them.
What to do first, before you buy anything
1. Find every place your applications call a model. For each one, write down the goal, the expected shape of the answer and the rules an answer must meet. If you cannot write the rules, you have found a call that is not being checked at all.
2. Count your retry loops. Note how many attempts each allows, what it sends back to the model on a retry, and whether it keeps the failed attempts. Differences between them are differences in behaviour your users already see. Figure 3 is the inventory you are taking: everything on its left side is code your team wrote and now maintains.
3. Trace the rule that changes most often. Pick it and follow what it takes to change it today: who edits it, how long until it reaches production, and how you would prove later which version was in force. That trace is the case for moving it into a versioned file, and it tells you how much a release-free change is worth to your team.
These steps cost nothing but time. They show whether your model calls are checked or merely hoped for, and they give you a concrete starting point whatever you decide next.
How C2Consultants does this
IntentSharp is our .NET library for exactly this, and the program scale of Cognitive Infrastructure, our method.
Developers express application behaviour as intent, apply it to ordinary .NET values and methods (text, records, lists,
a DataTable or JSON, or an [AIIntent] method that replaces one interpretation step inside a method you already have),
and move the changeable wording and rules into JSON or YAML intent files. It checks each answer against your .NET type
and your rules, retries with the findings within the limits you set, keeps every attempt, and keeps the last good
version when a saved edit is bad. It runs on .NET 8, 9 and 10 with OpenAI, Anthropic or any IChatClient, including a local
model on Ollama; data goes only to the model providers you configure, and the licence key is checked offline. Our
estimate for a typical business application is 20 to 40% less C# code; that is an estimate, not a measurement. One
honest limit: when the running application proposes an edit to a definition, the library applies it by default and
ships no review layer, so a team that wants changes reviewed before they run wraps the writer and holds the edits it
flags as widening. C2Empowered, our operations platform, is built on IntentSharp and applies the same separation at the scale of an organization.
IntentSharp 1.0 is on NuGet for .NET 8, 9 and 10, with documentation at intentsharp.com. It is commercial software, not open source, licensed from $349 per
developer per year with unlimited deployments, and you can evaluate it free for 30 days on development and test systems
at https://intentsharp.com/pricing.
Next chapter: A Tuesday, before and after. What changes for the people who do the work when the routine part of it runs through a governed platform.