In a regulated environment, the question that decides an AI engagement is almost never "how good is the model." It is "where does our data go, and how would you prove it."

Most answers to that question are a paragraph. The paragraph sits in a master services agreement, it uses the word "appropriate" twice, and everyone signs it. It is a promise, and a promise is a fine thing. But a promise does not execute at runtime. Configuration does.

I have been building a delivery-planning agent called Planwright. It reads an engagement's documentation and source code and drafts assessments, decision records, and roadmaps for a human to review. It is the kind of system that is trivially useful and trivially dangerous: to be useful at all, it has to read the client's most sensitive material. So the boundary is not a feature of the system. It is the system.

A boundary is only worth building before you need it. Retrofitting one after a security review has already said no is how these projects die.

Here is what I think a defensible boundary looks like, at the three points where it can fail.

Intake: secrets never travel

Before anything is indexed, the corpus is scanned. Credentials, keys, and connection strings that turn up are quarantined. Never indexed, never sent to any model, and reported back to the client.

That last clause is the one that matters. A scan of a working codebase usually turns something up, and the client should hear it from me rather than from an incident report.

Processing: fail closed, before the run starts

The agent's own instructions tell it not to send sensitive material to third parties. That is worth something, and it is not worth much. An instruction is a policy. Configuration is a control, and configuration is what actually decides where the bytes go.

So the guardrail lives in settings validation, not at runtime.

The sensitivity flag defaults to true. Fail safe, not fail open. A corpus is sensitive until a person deliberately says it is not.

Selecting a backend that can send data off the machine, for a corpus marked sensitive, raises an error before the run starts. Not a warning. Not a check at the moment of the call. The process refuses to come up.

Every index and cache is derived from the engagement's own directory. There is no shared location, so two engagements cannot contaminate each other. Not "should not." Cannot.

I considered a runtime warning and rejected it, because a warning is easy to ignore and one misconfiguration leaks the corpus. I considered trusting the operator and rejected that too, on the grounds that it is incompatible with being the point.

The consequence is that where processing happens becomes a contract term rather than an implementation detail. The client picks a posture, and each posture states its trade-off in plain language, including the unflattering half. Zero-egress local is the strongest boundary and the simplest approval story. It also means smaller models, which means some deliverables run under a stricter mandatory review tier and heavier jobs batch overnight. Frontier models under enterprise cloud controls get you full capability. They also mean the data leaves the client-controlled environment. And if the processing runs in my tenancy rather than the client's, then the client is trusting my account controls, which is exactly why those controls are named and auditable in the document rather than implied by it.

None of that is a selling point. It is the trade-off, written down before anyone has to discover it.

Exit: the part nobody writes about

Everyone in this field talks about how an engagement starts. Almost nobody publishes what happens when it ends.

Close-out is a command. It enumerates every store the engagement created and, on explicit confirmation, returns them and destroys them. The specifics are the whole thing:

Dry run is the default. Destruction requires an explicit confirmation flag.

Archive before destroy. The "returned" half of the promise is a real artifact, not a gesture.

Reserved names are refused outright, so a mistyped argument cannot take out the shared reference library.

A symbolic link that escapes the workspace is refused, not followed.

A receipt is written outside the tree that was just destroyed, recording every path, every size, every action, and every warning.

And one more, which is the part I am most confident about. It does not rewrite git history, and the receipt says so. If a contract requires that, it requires a separate, deliberate act by a person who knows what they are doing. A destruction tool that quietly rewrites history is worse than no tool at all.

What this buys

None of this makes the model better. It does not improve a single draft.

What it buys is that the engagement is approvable. In a regulated environment that is the binding constraint, and it sits upstream of everything else. Model quality does not matter if the security review says no.

So the differentiator is not the model. It is the boundary you can hand to counsel, and the receipt you can hand to an auditor.

The decision records behind all of this are public. They are numbered, they carry the alternatives I rejected and the reasons, and they are the closest thing I have to showing my work. The data-boundary guardrail is ADR_003. The review gate and provenance are ADR_005. The close-out receipt is ADR_022.

Planwright on GitHub