A coding agent builds your software, and the AI rules end up everywhere.
A coding agent that reads one intent file knows the goal, the data model, the rules and the budget. Declare what each operation needs and let the model decline rather than invent; test every operation on scripted replies with no key; keep the facts in an immutable ledger and the decisions beside it; give a long chain a time budget and read what each step cost.
What it costs today
A coding agent that adds a model call puts the goal in a string at the call site, the expected data in a class, the checks in an if statement and the retry in a loop. The next agent, or the next person, has to find all four before changing one, and nothing says which facts the operation needs, so a fact the input leaves out becomes a value the model invents and the type accepts.
Tests that need a key do not run in CI, so each change is checked by hand or not at all. A chain of calls with no budget fails late, and nobody can say which step spent the time or the tokens, or which step changed a fact it should only have read.
How IntentSharp handles it
The ranking below is one step of a job's estimate. Its input type names the fact the ranking depends on, the delivery mode, and its result may leave the order empty and list what is missing: with no delivery mode the model declines and the code asks, and with one it ranks. The chain after it gives each of four calls its own budget, a deadline for the whole, and a report of what each step cost.
/// <summary>One way to deliver a job's materials.</summary>public sealed record DeliveryOption(string Name, double Cost, int Days); /// <summary>What the ranking needs: the job's delivery mode, which decides what "best" means, and the options.</summary>public sealed record RankingInput(string? DeliveryMode, IReadOnlyList<DeliveryOption> Options); /// <summary>The options in order, or no order and the facts that were missing.</summary>public sealed record Ranking(IReadOnlyList<string>? Order, IReadOnlyList<string> Missing);AIResult<Ranking> ranked = await input.AIAsync<Ranking>(client, "Rank the delivery options, best first, for the job's delivery mode. If the input does not state a " + "delivery mode, return no order and list what is missing. Do not assume one.");if (!ranked.Success) return null;if (ranked.Value!.Order is null) Console.WriteLine($"Ask for: {string.Join(", ", ranked.Value.Missing)}"); // the model declined; ask, do not guessThe ranking, with no delivery mode in the input: Order: none Missing: delivery modeThe same ranking, with the delivery mode "needed on site tomorrow": Order: Courier, Pickup, Freight// The chain's own deadline. Each call keeps its own budget inside it.using var deadline = new CancellationTokenSource(limit);try{ return await chain.RunAsync(job, deadline.Token);}catch (OperationCanceledException) when (deadline.IsCancellationRequested){ // Every stage that finished is saved: the next run of this job starts at the stage that was running. return null;}A unit test that needs no key (13 lines)
[Fact]public async Task Urgency_is_read_from_the_reply(){ var provider = new ScriptedProvider("""{"value": 4}"""); using var client = ScriptedClient(provider); var result = await "The line stops on Thursday without the belts.".AIAsync<int>(client, "Rate the urgency of this order email from 1 (routine) to 5 (critical)."); Assert.True(result.Success); Assert.Equal(4, result.Value); Assert.Equal(1, provider.CallCount);}A run of job J-1042, on a scripted model: extract passed 1 attempt 333 tokens 4.0 s estimate passed 1 attempt 335 tokens 9.0 s review passed 1 attempt 390 tokens 3.0 s revise passed 1 attempt 413 tokens 8.0 s The chain: 4 stages, 4 model calls, 1471 tokens, 24.0 s. Estimate: 9 hours. The full chain, its checkpoints and its timings are in the long chains guide. Compiled and run with every build of this site; the model's answers are scripted, and a pass means the answer met the checks, not that it is right.
What stays in your control
The agent writes the intent, the data model and the rules. IntentSharp checks every answer against them, and your code keeps what to do with the result.
- One file per operation. An intent file holds the intent, its data model, its rules, its examples and its budget: attempts, a time limit and the tokens of each reply. An agent edits it like code, a person reviews the change like code, and a field the format does not know is reported, not ignored.
- The criteria are judgement. Whether the model declines or guesses is the model's judgement, which the intent's words guide. Only the type, the rules and your registered checks are checked in your process, and a pass means the answer met them, not that it is right.
- What may stay empty. A nullable member and a list of what is missing give the model a way to decline. A check of your own refuses what the model must not invent, such as a citation of a file the input does not hold, and its message goes back to the model on the next attempt.
- Tests with no key.
ScriptedProviderstands in for the model, with no network and no key, and records every request. The client on it is the real client, so a test sees the same checks, retries and limits a live call would. - Facts that do not change. The evidence ledger keeps each fact as it was recorded; a later answer is a record of its own that names the fact. Each step's intent says which component may change what, and a rule set refuses a write to the ledger's table.
- A budget per call, a deadline per chain. Each call has its own time budget, attempts and model calls; your own token stops the chain; each step's
AttemptsandUsagesay what it cost. Progress and checkpoints are your application's code. - Limits count calls, tokens and time. Not money. To bound a chain's spend, add up its calls'
Usageand stop when the total passes your limit.
Read next
Input contracts
Typed inputs that carry every fact, a result the model may leave empty, and a check that refuses invented values.
Testing
Tests on scripted replies and failures, with no model, no network and no key, and when a live run is still needed.
Evidence ledger
Facts written once, later answers as records of their own, and which component may change what.
Long chains of calls
A budget on each call, a deadline on the whole, checkpoints, and what each step cost.
A project built this way: A project estimator on IntentSharp. More problems and their answers: Solutions.
Give the agent one file to read per operation.
dotnet add package IntentSharp