Causality checks before a search
The gate truncates the series, replays the graph and compares the past bar by bar. A refusal carries what leaked, by how much, and where.
Output for past bars holds under truncation
A kernel is causal when its output for past bars stays the same as future bars are added or removed. The check runs the backend on the full series, then on the same series truncated at a cut, and compares what the two have in common.
The comparison covers the overlap strictly before the cut, minus the boundary margin. Equality is tested against a tolerance of 1e-12, and a NaN on one side matches a NaN on the other, so a window that has not filled yet is not read as a disagreement.
The tolerance is the contract, and the measurement is separate from it: on the shipped kernels the largest disagreement comes out at exactly zero, twelve orders of magnitude below what would be allowed. Publishing the looser number is what makes the tighter one mean something.
The gate is written against the backend interface and not against one engine. A slim engine today and a different backend tomorrow pass through the same test.
Open full sizeEach backend declares how far its own boundary reaches
A position held across the cut moves for reasons unrelated to reading ahead, so a margin is removed from the comparison. That margin is declared by the backend, and the gate takes it as given.
Some engine kernels return a daily P&L on an integer index. The comparison then uses a positional cut for this index type, which keeps the same invariant on a series that carries no dates.
What the gate runs on each user search
cut 0.66 of the series compare per-bar P&L, tolerance 1e-12 verdict refusal with the culprit named replay kernel alone, filters removed mono one instrument, longest series
The comparison also returns how many bars it actually compared. That count separates a clean result from a result where 0 bars were measured, and a gate blind to that difference would hand out an acquittal it did not earn.
Three answers, and the gate states which one it has
When the graph wires no filter, the message names the strategy kernel. When filters are wired, the kernel is re-probed alone: if it leaks by itself the kernel is named, and if it is clean the message places the leak in one of the wired filter nodes. The second answer is a set, and it is written as a set.
A third case exists. The gate replays the graph on a single instrument — the longest series available, since a short one can leave the compared overlap empty and produce a manufactured acquittal. When the leak fails to reproduce there, the message names two possible causes without choosing between them: a cross-sectional kernel that ranks names against each other, or the portfolio aggregation itself.
That case is not hypothetical. In a measured portfolio-divisor leak, the kernel stayed bit-identical symbol by symbol while an instrument that had not traded yet made the denominator depend on the future. When the single-instrument probe itself fails, the attribution is reported as undetermined instead of guessed.
A refused run produces a name, and no figure
The run stops at the gate. The message carries the culprit, the size of the disagreement and the timestamp where it first appears, followed by what to try next. No performance number is produced for a refused graph.
causality gate refused bars before the cut differ from the full-series replay re-probe without the filters: the strategy alone is leak-free → one of the wired filter nodes carries the leak
The validation that measures the shipped kernels against this invariant, and the deliberately non-causal control it must catch, are plotted on the backtesting page.
Open full sizeThe margin is also a blind spot, and it is measured
Removing a margin from the comparison removes what happens inside it. A backend that reads one bar ahead — the shortest leak there is — passes the production gate with a disagreement of exactly zero, because the bar it reads sits inside the seven-day margin.
A second validation replays the same test with the margin set to zero, which is what makes that blind spot a measured quantity. The validity domain of the gate is registered with the other limits.