“It probably flags everything.”
A legitimate feature with a larger importance score than the leaked one is audited on every run, and must come back clear. It does.
Hindsight release audit · run 20260729T044318-0be937
· case fraud-screening-post-authorisation-dispute
· verdict confirmed
· decision BLOCK
Payments · Fraud screening
A payments company wants to block fraudulent transactions in real time, before the money moves.
The record this page argues from
Every number above is read from this. Nothing is computed in the browser.
Loading the record...
What is being audited
synthetic datasetModel
fraud_screening_v1_leaky
The version being considered for release
Its id in DataHub
urn:li:mlModel:(urn:li:dataPlatform:mlflow,hindsight.fraud_screening_v1_leaky,PROD)
Feature examined
disputes_on_account
built by the pipeline
fraud_feature_pipeline — one column out of everything the model was shown
Where the feature came from
| Step | Table | Column | Status |
|---|---|---|---|
| 1 | disputes_after_authorisation |
dispute_filed_at |
rows appear after the decision |
| 2 | fraud_feature_pipeline |
disputes_on_account |
knowable at decision time |
| 3 | fraud_screening_v1_leaky |
— | knowable at decision time |
In plain English
It scored perfect in testing. When we rebuilt it using only information that existed at decision time, it dropped to solid. About 48% of its apparent skill came from seeing the future.
Imagine a student who aces a practice exam. Impressive - until you notice the answer sheet was sitting on the desk. The score was real; the ability was not. Retake the exam without the answers and you learn what they actually know.
How Hindsight knew
Nobody told Hindsight which feature was suspicious. It read
DataHub — the catalog that records every
table, column and pipeline in the company, and
which column was built from which. Following
that map backwards from the model showed one feature drawing on
disputes_after_authorisation — a table whose rows
only appear after the instant the transaction is authorised or declined.
Running on metadata recorded from a real DataHub instance. Connect DataHub to publish the finding back into the catalog.
What actually happened
The problem, on a calendar
Everything left of the red line was knowable when the decision had to be made. Everything right of it did not exist yet. Watch the bottom row cross the line.
the decision itself
all of it existed before the decision
stops at the decision - cleared for release
still collecting data 9 days after the decision
| Asset | Column | Availability | Status |
|---|---|---|---|
transactions_at_decision_time |
prediction_time |
ends at the cutoff | Source |
account_history_point_in_time |
account_age_days |
ends at the cutoff | Source |
fraud_feature_pipeline_safe |
account_age_days |
ends at the cutoff | Clear |
fraud_feature_pipeline |
disputes_on_account |
extends 9 days past the cutoff | Violation |
How certain is this?
Either one is enough on its own. Hindsight reports which fired rather than blending them, so you can see exactly what settled it.
Why you can believe this
“It probably flags everything.”
A legitimate feature with a larger importance score than the leaked one is audited on every run, and must come back clear. It does.
“It can only ever say no.”
One scenario is expected to pass, and does — the same model after the repair. A gate with no “yes” is not a gate.
“The numbers were tuned to look good.”
The data generator is frozen and its seed committed. Whatever it produces is published — including a confirmation policy that was missed by 5.1 points, disclosed rather than hidden.
“An LLM decided this.”
It cannot. A language model may explain the evidence; reaching a verdict runs through deterministic code with no model in the path.
Why nobody noticed
The dispute table is joined on account ID with no time filter at all. It looks like any other account attribute.
What it costs
The model appears near-perfect in evaluation and then blocks almost nothing in production, because at authorisation time the dispute count is always zero.
What to do now
Pre-production release audit
Every claim above traces to a measurement below. Nothing here is inferred by a language model.
Release decision
BLOCK Deterministic verdict: confirmedWhat the agent actually did
Every DataHub call, SQL check and validation step, in order. Entries marked LIVE hit a real DataHub instance during this request; RECORDED entries replay responses captured from a real instance so this page works without Docker.
Directional column lineage
The timeline above the fold shows this on a calendar. Here is the exact finding from the transformation itself:
The post-outcome source is referenced, but no directional available_at <= prediction_time cutoff was found. The transformation does not reference available_at.
Calibration regression fixture ยท constant across scenarios
This controlled regression pair is deliberately held constant across audits: it proves the gate does not confuse importance with illegal availability.
The longer bar is the safe one. Hindsight blocks the shorter feature and clears the longer, because importance measures whether a feature is useful — never whether the information was allowed to exist yet. A detector built on ablation flags exactly the wrong one here.
| Feature | Ablation delta | Verdict |
|---|---|---|
days_since_last_payment |
0.356811 | confirmed |
prior_delinquencies |
0.279669 | clear_for_release |
Counterfactual test
48.1%
of the apparent feature advantage disappeared
AUC 1.000000 is expected: this synthetic planted leak is total by construction. Real leakage can be subtler, and the generator is frozen rather than tuned to look realistic.
False-positive defence
The legitimate account_age_days feature remains predictive because it existed
before the decision. It must come back clear on every run.
Inspectable execution
Smallest safe repair
evt.available_at <= src.prediction_time
The proposal is verified independently. Hindsight never merges or applies pipeline code automatically.
Human approval boundary
Dry-run is the default. Explicit approval writes the field tag, structured verdict, linked audit Document and active incident — then re-reads every one to prove it persisted.
Write-back is disabled on the public demo. Against a live instance this form publishes four records once a person ticks the approval box: a tag on the offending column, the verdict as a structured property, an audit document, and an open incident. Each one is read back afterwards, and a write that cannot be re-read is treated as a failure.
Clone the repository and run uv run hindsight serve against your own
DataHub to exercise it. The example record it produces is in
examples/audit_document.md.