Skip to main content

Leakage gates

Target leakage is the classic silent failure of applied ML: a model that scores brilliantly offline because one of its inputs is the answer, then collapses in production where that input doesn't exist yet. Tessera's leakage gates are the structural defense — and they are build-aborting: a violation stops the dataset build with a typed report; it never ships a poisoned model with a warning attached.

A worked example: e-commerce​

An e-commerce team defines repeat_purchase_30d as a label and builds a dataset. Four things their data could plausibly smuggle in, and the gate that catches each:

1. Label-shaped column names → deny name patterns​

Their order events carry is_repeat_purchase — a column their analytics team back-filled after outcomes were known. The platform's always-active deny patterns match label-ish names (label, target, outcome, ground_truth), and the project adds its own vocabulary:

{
"deny_name_patterns": ["(^|_)repeat_purchase(_|$)", "(^|_)ltv_bucket(_|$)"],
"identifier_value_scan": true,
"label_temporal_gate": true
}

Name patterns only ever deny — there is no pattern that allow-lists an attribute into the model. Test a proposed name against the current policy with POST /leakage-policy:test before you ship it.

2. Explicit denies​

The catalog team knows warehouse_margin_note is written by a nightly job that reads returns data (i.e., the future). One entry in deny_attributes: [{event_type, attr_key}] and it can never be role: feature, whatever it is named.

3. Identifiers hiding in text → value scanning​

A support-ticket attribute contains "please contact u_00417 about this order" — the entity's own id inside a free-text feature. Identity is never a feature, and at build time every text attribute's values are scanned for known identifier values; a hit aborts the build (identifier_value_hit). The violation's evidence counts the identifier values found (value_count) and lists up to five of them only as keyed digests (value_digests, each hash: followed by 64 hex characters). The report never repeats an identifier itself.

4. Post-outcome attributes → the temporal gate​

A resolution_code attribute appears on events only after an order's outcome is recorded and predicts it perfectly. The temporal gate flags features whose values perfectly predict a label while first appearing after the label's observed_at (heuristic; warn-level in Phase 1). A warn-level entry is a finding, not a refusal: the build succeeds, the dataset page lists it under Findings, and you can still start a run on the dataset. The run form shows the findings first, and the run's run.create audit event records them as dataset_findings. Review the attribute's role in the contract before you rely on the model.

Fail closed, with receipts​

When a gate trips, the build ends in status aborted_leakage and the dataset's violation report says exactly what and where:

{
"violations": [
{
"type": "deny_name_pattern",
"event_type": "order.placed",
"attr_key": "is_repeat_purchase",
"pattern": "(^|_)repeat_purchase(_|$)"
}
]
}

Fix the contract (usually: flip the attribute to excluded or target_only) and rebuild. A clean build records its empty violation report in the manifest, and the leakage-policy version it passed under is pinned by fingerprint into the dataset and every bundle trained from it. There is exactly one implementation of the policy evaluator, imported by every enforcement point — the gate you tested against is the gate production runs.

Next: Fingerprints and reproducibility.