The blocker everyone names and nobody fixes

The reason data quality stays at the top of the blocker list year after year is that most of the work aimed at it never touches the thing that breaks. A quality dashboard tells you a field is null 4% of the time today; it does not tell you that the upstream team is about to change what that field means next Tuesday. A cleanup pipeline scrubs a known defect; it does nothing for the defect that arrives the first time a producer ships a schema change they had no reason to think anyone downstream depended on. These are point-in-time tools fighting a relationship problem. The agent depends on data it does not control, produced by a team that does not know the agent exists, under no obligation to keep anything stable. That is not a data-quality gap. It is a missing contract.

We have made the broader version of this argument before — the foundation, not the model, is the binding constraint on whether an agent can run in production. This piece zooms into one layer of that foundation, because it is the one most teams skip and the one that fails most expensively. A schema registry without enforcement, a lineage diagram nobody updates, a Slack message that says "heads up, we changed the orders table" — these are the implicit handshakes that hold right up until the moment an agent is wired to act on the output, at which point every informal assumption becomes a production incident waiting for a release to trigger it.

A contract is not a schema

The word that trips people up is "contract," because they hear "schema" and assume they already have one. A schema describes the shape of the data right now. A data contract is a promise about that shape over time, with consequences attached when the promise breaks. The distinction is the whole point. A schema says this column is a string. A contract says this column is a string, it will stay a string, you will get 30 days' notice before that changes, it arrives by 06:00 UTC daily, it is complete to 99.5%, and if any of that is violated here is who owns the fix and here is what the consumer does in the meantime. One is a snapshot. The other is an agreement between two named parties — a producer and a consumer — with terms, an owner, and a defined failure path.

Concretely, a data contract carries four things a schema does not. A strict version, so a consumer can pin to a known shape and a producer can evolve without silently breaking anyone. A set of SLAs on the dimensions an agent actually depends on — freshness, completeness, distribution stability — not just structure. A breaking-change policy that says what counts as breaking, how much notice is owed, and how versions run in parallel during a migration. And a fallback: the defined behaviour when the contract is violated, so the consumer degrades on purpose instead of guessing. Strip any one of those and you are back to a schema with good intentions.

A schema tells the agent what the data looks like today. A data contract tells it what it can rely on tomorrow. An agent can only act safely on the second — and most pipelines only publish the first.
Dimension The implicit handshake The data contract
Shape Whatever the table happens to be this week. A versioned schema; consumers pin to a version.
Change Ships when the producer is ready; downstream finds out by breaking. Governed by a breaking-change policy with notice and parallel versions.
Timeliness Usually on time. No promise, no alarm when it isn't. A freshness SLA the agent can read before it acts.
Ownership Diffuse — "the data team," nobody in particular. A named owner per dataset, on the hook for the terms.
On violation The agent decides anyway, on data it shouldn't trust. A defined fallback: hold, degrade, or escalate — on purpose.

The class of bug it eliminates

There is a specific failure that data contracts exist to kill, and it is worth naming precisely because it is so hard to debug after the fact: the agent decided wrong because the data changed without warning. Nothing in the agent broke. The model is the same, the prompt is the same, the code is the same. An upstream field changed units from cents to dollars, or an enum gained a category the classifier had never seen, or a join started returning duplicates after a pipeline refactor — and the agent, which had no contract telling it otherwise, processed the new shape with logic built for the old one. The output looks plausible. It passes whatever shallow validation exists. It is wrong. And by the time anyone notices, the change that caused it is twelve commits deep in a repository the agent's owners never read.

This is the same silent-failure pathology we wrote about in the agent reliability gap: a system that fails loudly is annoying but governable; a system that fails silently erodes trust without ever raising an alarm. Most reliability work focuses on the agent's own behaviour — evals, guardrails, observability on the decision. The data contract pushes the guardrail one layer up, to the input. It turns "the data changed without warning" — an undetectable, untraceable category of error — into "the contract was violated at 06:14, here is the field, here is the version that broke it, here is the owner, and the agent held the affected cases instead of deciding on them." The bug does not disappear. It becomes visible, attributable, and recoverable — which is the entire difference between a defect and an outage.

The emerging frame, component by component

The pattern that teams running agents on contracted data converge on is consistent enough to enumerate. None of the pieces is exotic; the discipline is in having all of them, because each one covers a gap the others leave open.

  • A schema registry with semantic versioning — the contract lives in a registry, not a wiki, and every version is tagged under semver so the difference between a safe additive change and a breaking one is encoded, not argued about in a thread. A consumer pins to v2; a producer shipping v3 with a removed field knows, mechanically, that it broke compatibility.
  • A formal owner per dataset — a named person or team accountable for the contract's terms, not a diffuse "data team." Without an owner there is no one to give notice, no one to call when freshness slips, and no one whose roadmap the breaking-change policy binds. Ownership is what turns a document into an obligation.
  • A deprecation policy — the rules for retiring a version: how much notice is owed, how long the old and new versions run in parallel, and when the old one is finally switched off. This is what lets data evolve at all without every change becoming a fire drill for every consumer at once.
  • An in-pipeline validator — enforcement at the point of production, so data that violates the contract is caught before it lands in the table the agent reads, not after the agent has already acted on it. A contract checked only in documentation is a suggestion; a contract checked in the pipeline is a gate.
  • A broken-contract alert — when a validation fails or an SLA is missed, someone is paged and the affected consumers are notified, with enough context to act: which field, which version, which SLA, who owns it. The alert is the difference between a violation that gets fixed in minutes and one that gets discovered in a quarterly review.
  • Integration with observability — the contract's state feeds the same place the agent's behaviour is monitored, so a drop in output quality can be traced back to an input violation rather than investigated as a model problem. This is the link that closes the loop between "the agent got worse" and "because the data did."

Four ways data contracts go wrong

The failure modes are as predictable as the components. Each one is a version of keeping the form of a contract while dropping the part that makes it bind.

  • Contracts that live only in documentation, with no enforcement — the team writes a beautiful schema spec, links it in the wiki, and never wires a validator into the pipeline. Nothing checks that the data actually matches the contract, so the contract drifts from reality the first time a producer ships without reading it. A contract nobody enforces is a description of how the data used to behave. The test is blunt: if a producer violates the contract today, does anything fail? If not, you have a document, not a contract.
  • No feedback path from consumer to producer — the producer publishes the contract and treats it as a one-way declaration, with no channel for the agent's owners to say "we depend on this field staying stable" or "this SLA is too loose for what we're doing." Contracts written without the consumer's requirements encode the producer's convenience, not the agent's needs — and the first the producer hears of the mismatch is when the agent breaks on a change the producer thought was harmless.
  • One schema serving consumers with divergent requirements — a single contract is stretched across an analytics dashboard that tolerates day-old data and an agent that acts on it in real time. The freshness SLA gets set to whatever the laxest consumer accepts, and the agent inherits a guarantee that is too weak for what it does. Different consumers of the same dataset have different reliability needs; pretending one contract fits all of them quietly under-protects the most demanding one.
  • No parallel version during migration — the producer needs to evolve the schema, so they change it in place and flip every consumer at once. There is no window where v2 and v3 both run, so every downstream agent has to coordinate its cutover to the same instant — which never happens cleanly, and the migration becomes the outage. The whole point of versioning is to decouple the producer's change from the consumer's adoption; skip the parallel window and you have versioning in name only.

The trade-off, stated honestly

Data contracts are not free, and the cost lands exactly where it is most felt: the speed at which a schema can change. The implicit handshake is fast precisely because it is unaccountable — a producer who owes no notice and runs no parallel version can rename a column in an afternoon. Put a contract around that column and the same rename becomes a versioned change with a deprecation window and a migration plan. For a dataset no agent depends on, that ceremony is pure overhead, and applying it everywhere is how a data-contract initiative dies of its own weight — the team drowns in governance for tables nobody acts on.

So the honest framing is not "contract everything." It is "contract the data an agent acts on, and leave the rest informal." The dimension that decides is consequence: the more autonomously an agent uses a dataset — and the more expensive its mistakes, which for anything touching an invoice, a claim or a compliance decision is very expensive — the more the contract earns its overhead. A dataset feeding an exploratory dashboard can stay on a handshake; a dataset feeding an agent that writes to a system of record cannot. The trade-off is real velocity against real safety, and the right answer is not a global policy but a line drawn at the point where a wrong decision starts to cost more than a slower schema change. The teams that get this right spend their contract budget where the blast radius is largest and nowhere else.

Closing thought

Software learned this lesson a decade ago and gave it a name: APIs have contracts, versions, and deprecation policies precisely because the alternative — every service free to change its interface whenever it liked — does not scale past a handful of teams. Data is now in the same position software was in then, and for the same reason: it has acquired a consumer that acts on it autonomously and at volume. An agent is, in the end, a program calling an interface, except the interface is a dataset and the call is a decision with a consequence. Treat that interface like the uncontracted, unversioned, unowned thing most data still is, and you build an agent whose reliability is hostage to every upstream sprint it never sees. The team that skips this layer does not avoid the cost — it defers it, and discovers six months in that a meaningful share of its agent's outcomes were produced from data that had quietly drifted outside the contract nobody wrote down.

At Cogneris we treat the document as a contracted input, because an extraction pipeline is exactly the case where the data and the decision sit on top of each other. Every template defines the schema the downstream agent depends on — the fields, their types, the validation that has to hold — and that schema is versioned, so a change to what a document class produces is an explicit, reviewable event, not a silent drift that surfaces as a wrong decision weeks later. When an incoming document violates what the template promised — a missing field, an out-of-range value, a layout the contract did not anticipate — the case is held and routed to review rather than passed downstream as if it were clean, and the audit trail records which version of the contract every document was processed under. The result is a foundation an agent can act on: not data that is merely present, but data whose shape, freshness and meaning are promises you can read before you act on them. If you are wiring an agent to your document flow and want the input layer to be a contract instead of a hope, talk to our team.