Sixth in a series on building agents that survive regulatory review. Previously: delete your approval rule, a checkpoint is not evidence, interrupt() is a pause, not an approval, the failure no guard catches, a test suite nobody has watched fail.
We build systems; your counsel interprets the regulation. This one is about engineering, and it’s the post where I’m arguing against my own commercial interest.
Two different documents
The exit clause says you may terminate, that the provider will assist, that data will be returned in an agreed format, that there is a transition period. Your legal team wrote it, the vendor’s legal team amended it, and it is genuinely useful — it establishes obligations.
The export is a file. It arrives on a Tuesday. Somebody has to open it.
The gap between them is the entire subject. A clause describes what you are owed; the export determines what you actually have. And here’s the property that makes it different from most vendor risk: it cannot be tested by reading the contract. Only by receiving the file.
What arrives, in practice
Three shapes, in ascending order of how much you can do with them.
Framework-native state. Checkpoints in whatever serialisation your vendor’s orchestration library uses. Pickled objects, or a schema that only makes sense with the library’s type definitions loaded. To read it you need the framework, the right version of it, and often the vendor’s own node definitions to interpret which channel meant what.
This is a copy of your dependency. It satisfies the clause and resolves nothing.
A database dump. Better. Tables, rows, foreign keys. Readable with standard tools. Whether it answers questions depends on whether the schema records decisions or only current state — which is the [previous post’s] argument arriving here in a different form. A dump of current state tells you where the system was, not what it decided.
An append-only record of what happened. Text or JSON, one row per event, in chronological order, with the reasoning and the identities attached. Openable with a standard library. Answers questions without any of the vendor’s code present.
The difference between the first and the third isn’t effort. It’s a decision made at the start of the project about what the system stores, and — as with everything in this series — it cannot be made retroactively, because the information either was written down or wasn’t.
The four-hour test
Concrete, and you can run it against any vendor before signing.
Ask for an export of a test account with a few weeks of real activity. Not a sample file they prepared. Then, on a machine with no vendor software installed:
- Open it with the standard library of any language you like.
- Pick one decision the system made. Answer: what did it have in front of it, what did it choose, what rule permitted it, who could have stopped it, did they.
- Count how many of those five you can answer.
- Now do it for a decision three months older than the oldest one in the export.
Step four is the one that catches things. Many systems retain full detail for thirty days and summaries beyond. The clause says “your data”; the retention policy says which data still exists. Those are different documents and they are rarely read together.
If you need the vendor’s code for step one, the clause bought you a file.
The awkward part: this is an argument against vendors
Including us.
The honest position is that the ideal arrangement for you is one where switching costs us nothing to impose and nothing to defend — where your record is portable by construction, where the value we provide is the work rather than the lock-in, and where leaving is a Tuesday rather than a project.
Vendors do not usually volunteer this, because the commercial instinct runs the other way. Proprietary state formats are a retention mechanism. They’re rarely designed as one — they’re usually just what came out of the framework — but they function as one, and nobody is in a hurry to fix a bug that increases renewal rates.
So here is the concrete version of what I think you should demand, from us or anyone:
The record is yours, in a format you can read without us. Append-only, one row per event, human-legible. If we vanish, you open it with json and a text editor.
The runtime is inspectable. Not “open source” as a marketing checkbox — actually readable by your reviewer in an afternoon. Ours is about 5,700 lines with no runtime dependencies, because a dependency is a thing your reviewer also has to read.
Nothing in the record requires interpretation by our code. If a field’s meaning lives in our type definitions, it doesn’t survive us.
Your prompts, tools and policies are configuration you hold, not something compiled into our system.
You should be sceptical of a vendor who makes those claims without letting you test them. Which is the point of the four-hour test: it converts a claim into an observation.
The second-order dependency
One more layer, because it’s the one people miss after getting the first one right.
Suppose your export is clean. Portable, append-only, readable.
Who operated it? If the system ran in your vendor’s cloud, with their credentials, against their queue, then your export is a complete record of a system you cannot run. You have the evidence and not the capability.
Three questions that separate these:
Where did it execute? Your infrastructure or theirs. This is the difference between “we stop paying them” and “we rebuild”.
What credentials did it use? If tool access ran through the vendor’s keys, every integration breaks at termination and the record shows you exactly what you can no longer do.
Could you run it Monday? Not “could you rebuild it in six months”. Could you, with the export and the documentation and no vendor staff, start it up.
The last one has an honest answer for most arrangements, and it’s usually no. Which is fine — that’s what a transition period is for. But it should be a known number rather than a discovery.
Concentration, which is the part nobody costs
Briefly, because it belongs to the same family and is generally handled worse.
Your agent runs on a model from one provider. If that provider deprecates the model, changes pricing, or restricts your use case, what happens?
The engineering answer is an abstraction: a thin protocol, several implementations, a switch. Cheap to build — ours is about forty lines — and it converts a dependency into a preference.
The part that isn’t cheap: your prompts are tuned to one model’s behaviour. Swapping providers is a config change and a re-evaluation, and without a labelled set you cannot do the second part. Which is the previous post arriving here: the eval set is what makes the abstraction real rather than theoretical. Without it, “we could switch providers” means “we could change a string and find out”.
The exercise
For your current AI vendor, in writing, answer four:
- If they terminated tomorrow, what file arrives?
- Can you open it without their software?
- Can you answer “what did the system decide on [date three months ago], and why” from it?
- Where does the system execute, and whose credentials does it use?
Then ask them the same four and compare. The delta is your actual exposure, and it is usually larger than the clause suggests.
Where this fails in our own work
Retention and redaction. Our record keeps everything verbatim, for ever. Correct default for evidence, wrong one for personal data. Field-level redaction at write time, with replay that still works afterwards, is the piece we’re building; today it’s a decision at ingestion, which is weaker. This is the gap I’d raise first if I were reviewing us.
SQLite. Single node. Fine for the volumes we’ve run, a ceiling you’d hit before you hit most others, and a migration you’d rather do before you need it than after.
Metrics. Logs only. Nothing to graph, nothing to alert on. You’d wire it to your own observability stack, which is arguably correct — but it means “we’d know” is currently “we could find out”.
Changelog this month: our own export path grew a verify command that derives run state two ways — replaying the whole log, and reading the fast projection — and asserts they agree. Before that we believed the cache was right. Now it’s demonstrable, which is the same distinction this entire series keeps arriving at.