Two records arrive. Same first name, same last name, birthdates one digit apart, one has a middle initial and the other doesn't, the addresses are four years and two cities apart.
Are they the same person?
Say yes when the answer is no, and you have just put one person's medication list, allergies and problem history into another person's chart. Say no when the answer is yes, and a clinician is about to make a decision using half of what they needed. There is no safe default. There is no option to decline.
Both errors are wrong answers with a body attached.
This is the class of work agents are now being pointed at, and almost nothing written about agent engineering this year is written for it.
Harness, as the word is being used this year: the deterministic runtime wrapped around a language model. The model proposes an action; the harness decides whether it is well-formed, permitted, affordable and safe, executes it, and hands the result back. Everything an agent does that you can actually reason about happens there rather than in the model.
Most of what has been written about building one assumes the worst case is an answer you throw away. Retry it, log it, adjust the prompt, move on. That assumption holds for a summariser, a coding assistant, a research tool — the whole class of systems where being wrong costs a few seconds and some tokens.
It stops holding the moment the output leaves the building. Eligibility checks, claim submissions, prior authorisations, record matching, care-gap outreach: these are the workloads agents are being sold into right now, and each one ends with something happening to somebody who has no idea your agent exists.
Thirteen opinions about who you are
In Part 1 I wrote about a harness my team built in 2006 inside an ambulatory EHR, and what happened to it when the rules underneath moved. The same system has a second lesson, and this one is about consequence rather than time.
Somewhere in that codebase is a weighted patient matcher. About twelve hundred lines: compare names, compare dates, compare address fragments, weight each agreement, sum the score, decide.
It appears thirteen times, in eight different versions.
It lives as a class copied into thirteen locations rather than shared from one — the lab result parsers, both clinical document exchange paths, several import routines — each copy drifting from the others as somebody fixed a bug in one place and not in the other twelve.
I went looking for that matching logic expecting a stored procedure. Identity resolution is the kind of thing you write once, carefully, and call from everywhere, and the schema had 2,115 procedures to choose from.
The procedures held none of it. The logic was sitting in the application code, thirteen times over, and the copies had been drifting apart for six years.
Thirteen copies of a matcher means thirteen different answers to are these two records the same human being, depending on which door the data walked in through. A lab result and a clinical summary describing the same patient could reach different conclusions on the same afternoon, and nothing in the system was positioned to notice the disagreement.
Meanwhile the application's own duplicate detection — the one a registration clerk actually sees — was exact-match only, and scoped to a single provider. Two clinics in the same practice, same patient, no flag.
The engineering mistake is ordinary. Somebody needed matching logic in a hurry and copied the file. What makes it worth writing down is what the mistake was about. Of all the logic in that system to have thirteen drifting versions of, this is the one that decides who someone is.
The axis that actually matters
The current guidance sorts agent actions into risk tiers — read-only, draft, external write — and gates the dangerous ones behind approval. That is correct, and you should do it. It is also too coarse for this kind of work, because it sorts by what the action touches rather than by what happens afterwards.
Two questions do more work:
Can it be undone? And who absorbs it if it can't?
| The system absorbs it | A person absorbs it | |
|---|---|---|
| Reversible | Retry. Log it and move on. | Apologise, fix it, tell them. Recoverable. |
| Irreversible | Expensive. Engineering problem. | This is the quadrant. Everything below is about this quadrant. |
A merged chart is in the bottom right. So is a denied claim that starts a clock, an eligibility answer someone acts on at a pharmacy counter, a cancelled authorisation, a notice that goes in the post. The common property is that the harm lands on somebody who was never in the room, and finding out later doesn't give them the day back.
Un-merging two patients is technically possible and practically brutal. Every downstream system that consumed the merged record has to be told, and some of them cannot be. Anyone who has done one remembers it.
The same seat, twenty years later
Nothing about that problem is historical. Put a 2026 agent in the same seat and the shape is identical, with one difference that cuts the wrong way.
Give a model two patient records and ask whether they describe the same person, and it will do a creditable job — better than a weighted-sum matcher on exactly the cases that matcher fails. It will work out that R. Karthikeyan and Karthikeyan Rajendran are one person, whose father's name got expanded out of an initial by a registration clerk who needed something to put in the last-name box. That Krishnamoorthy and Krishnamurthy are one surname that crossed into English twice. That a birthdate with the month and day transposed is a data-entry convention rather than a different human being.
A weighted-sum matcher cannot reach any of that, because it is built on an assumption the record quietly violates: that a name decomposes into a first, a middle and a last, and that agreement on each can be scored. Millions of patients in American practices carry names that were never shaped that way, and the field the clerk had to fill decided which fragment landed where. Every copy of that matcher scores the same two records differently depending on which form built them.
Then it writes a paragraph. These records most likely refer to the same individual, given the strong agreement on name and the plausible address history.
Most likely. Given. Plausible.
The old matcher was worse at the judgment and better at the handover. It produced a number, and a number can be compared against a threshold, stored, trended, and argued about in a design review. A paragraph can only be read — and whatever reads it next has to decide what most likely was worth, usually by asking another model, which is where the uncertainty quietly becomes certainty.
The capability went up. The seam it hands across got worse.
Thirteen drifting copies at least disagreed in a form you could diff.
Seven things the harness has to do
Every one of these lives in the deterministic layer around the model — the only place you have real control, and the only place any of this actually gets solved.
1. Uncertainty has to be a field, not a sentence
An agent that writes "I'm not entirely certain about this match" into its response has told you nothing your harness can act on. Prose confidence is unbranchable. You cannot route on it, threshold it, trend it, or alert when it drifts.
The matcher in that old system got this part right, structurally: it produced a score. A score can have a band. A band can have a policy.
Make the agent's output a typed proposal with the uncertainty as a first-class field — a score, a band, the specific evidence that moved it, and what it would take to resolve. Then the routing decision belongs to the harness, where it is testable, tunable and auditable, rather than to a sentence in a paragraph.
Record the score even when you act on it automatically. The distribution of scores over time is the single best early warning you will get that something upstream has changed.
2. Absence of data is a decision, and it has to be declared
Buried in that same system is a property on each clinical parameter called absence_satisfies. It answers one question: when we have no data at all for this, does the condition count as met?
It is set deliberately, and only on the screening and immunization parameters. That is the difference between a mammogram reminder that surfaces every woman who is overdue, and one that silently skips everyone who never had one — because the absence of a result looked, to the query, like nothing to do.
The same system distinguishes three states most software collapses into one. Eleven thousand rows recording family history unknown. Two hundred and ninety-three thousand recording no known habits. Not asked, asked and unknown, and asked and none are three different facts about a human being, and only one of them is silence.
Agents collapse all three constantly. A tool returns an empty array and the model reports that the patient has no allergies. What it actually knows is that a query returned nothing.
| What the tool returns | What it could mean | What the agent says |
|---|---|---|
[] | The patient has no known allergies | "No allergies" |
[] | Nobody has ever asked them | "No allergies" |
[] | The allergy service timed out | "No allergies" |
Make your tools return the difference. A harness that cannot distinguish no from no data will eventually tell someone they have no coverage when what it means is that it couldn't reach the payer.
3. Draft everything, commit narrowly
The draft-then-commit pattern is the one piece of current guidance that transfers to this quadrant without modification. The agent produces the full proposed action — the merge, the submission, the letter, the denial — as an object that exists, can be read, can be diffed against what is on file, and has done nothing yet.
The commit is a separate, small, boring, deterministic step.
The discipline is in keeping that second step dumb. Every time the commit path acquires a little judgement of its own — a fallback, a retry with modified parameters, a "if the primary fails, try the secondary" — you have moved decision-making into the one place you were relying on to have none.
4. Human review is a queue, with an owner and a due date
The old system had eleven separate work queues, each with its own schema and screens. It had none for failure.
Three hundred and forty-six thousand scheduling import errors in a log file. Forty-four thousand claim errors in a table. A hundred and sixty-seven visits that silently never got a billing code because an exception was caught and dropped.
The thing that stayed with me was the ratio. Eleven carefully-built queues for work somebody wanted done — refill requests, lab review, unsigned notes, incoming documents — each with its own screens, its own schema, its own procedures. Real investment, repeated eleven times.
And not one queue for work the system had failed to do. That work existed. It just had nowhere to be seen from.
Every one of those numbers is work a human being needed to do, and none of it reached a person by design.
An approval prompt that fires in a terminal at two in the morning, in a session nobody is watching, records only that the system technically asked.
Real review needs a durable work item: type, subject, owner — a person or a role — status, priority, due date, and the provenance of how it got there. If it can't be picked up tomorrow by someone who wasn't there today, what you have built is a pause with extra steps.
Then make every agent failure a work item too. Eleven queues collapse into eleven filters over one model, and the uncertain match, the timed-out tool and the rejected proposal all land in the same inbox as everything else a human owes the system.
5. Overrides are permanent objects
That old system treated a clinical override as a first-class record with its own lifecycle. A patient who has had a bilateral mastectomy should stop receiving mammogram reminders — not this month, not until the next rule refresh. Permanently, with a reason attached.
Without that, decision support gets switched off inside a month. Not by a policy decision, by attrition: the alert is wrong, somebody dismisses it, it returns tomorrow, and within three weeks the entire category of alert is noise that everybody clicks through.
Your agent has exactly this failure mode. The first time a reviewer rejects a proposal, your harness needs somewhere durable to put why, in a form the next run can read. Otherwise the agent proposes it again on Thursday, and the reviewer learns that rejecting things accomplishes nothing.
The reviewer is the scarcest component in the entire system. Design for their patience the way you would design for a rate limit.
6. The answer has to carry the question's version
One of the genuinely excellent things in that 2004 schema is a foreign key on every recorded clinical result, pointing at the version of the field definition that was on screen when the value was entered. Six million rows of definition history exist so that a note written in 2005 still reads the way it was written, under the labels that were in front of the person writing it.
Most systems still get this wrong, and every agent system I have looked at gets it wrong.
When your agent decides something, the record of that decision needs to carry the version of everything that produced it: the prompt, the rule set, the tool schemas, the model. Eighteen months from now, somebody will ask why a particular claim was submitted the way it was. Reconstructing the answer from a timestamp and a git history is guesswork dressed as an audit trail. This is the same argument as Part 1, arriving from the other direction: there, so you can tell when a rule has expired; here, so you can explain a decision to the person it happened to.
7. Disagreement is a signal, and something has to be listening
Thirteen copies of that matcher produced thirteen answers, and the deeper failure was that no part of the system stood in a position to notice when they differed. The copies could contradict each other all afternoon and the contradiction went nowhere.
You will have the same problem with fewer copies. The same two records are judged by the eligibility agent on Monday and the records agent on Thursday, against a model that was updated in between, and they reach different conclusions. Nothing in an ordinary architecture compares them.
Store the score as a fact, keyed to the pair of records it judged, rather than letting it evaporate once the decision is made. Then a disagreement becomes a query you can run, and a rising disagreement rate is the earliest warning you will get that something moved underneath you — a model version, a tool response shape, an upstream feed that started truncating middle names. The alternative is hearing about it from the person whose chart was wrong.
Autonomy belongs to the action, not the agent
The maturity ladders in circulation this year set an autonomy level per agent — level one approves every external write, level two approves plans, and so on up to fully autonomous.
That framing puts the dial in the wrong place. Almost no real agent sits at one level, because almost no real agent does one kind of thing. Reading a chart, drafting a summary, proposing a match and submitting a claim are four different consequences, and the same agent does all four inside a single task.
Set it per action. Every tool in the registry declares what it makes true in the world, whether that can be undone, who absorbs it if it can't, and what evidence the harness needs before it runs unsupervised. Then the same agent reads freely, drafts freely, proposes a merge above a confidence band and queues it below one — and never, under any circumstances, submits without a named human on the record.
That registry is also the most useful document your team will have. It is the only artefact that answers, in one page, what this thing is allowed to do to people.
What this is really about
I've written before about the scarce ingredient in healthcare AI, and about the five minutes a clinician loses to a keyboard. Both come back to the same thing, and so does this.
The hard part of these systems was never the reasoning — it is that the domain has edges you only learn by standing in it. That the absence of a lab result and a normal lab result are different facts. That stopping a medication is a clinical event and never a delete. That a person can be their own duplicate. That an alert dismissed three times has already lost.
Nobody encodes those by accident. They get encoded because somebody who has done the work sat down with somebody who can build, and neither of them assumed they already understood the other's job.
Thirteen copies of a matcher is what it looks like when that conversation doesn't happen — when identity resolution is treated as a utility function to be copied rather than the most consequential decision in the building.
The model will keep getting better, and it will still not learn any of this on its own, because none of it is in the training data. That knowledge lives in people who have watched the failures land.
Build the harness around what it costs to be wrong about a person. Everything else in the architecture follows from that number.
Part 1, The Rules That No Longer Exist, is about what happens to a harness when the rules it encodes are withdrawn underneath it.






