Skip to content
Back to skills

Principle No Lookahead

ASecurity

Apply to any evaluation, backtest, benchmark, grader, or training run over data with a time axis or an ordered decision sequence. Score a decision only against inputs that existed before that decision was made. Covers what the field calls look-ahead bias, target leakage, point-in-time correctness, and repainting. Reach for it on: a backtest or walk-forward run, a feature computed over a full history, a retrieval or agent eval, a label assigned after the outcome was known, or any result that l...

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 19, 2026
ai-agentsgogitperformance

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 19, 2026

npx -y skills add EdbertChan/catstack --skill principle-no-lookahead --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Principle No Lookahead?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Principle No Lookahead
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/edbertchan-principle-no-lookahead/badge)](https://www.skillsdirectory.com/skills/edbertchan-principle-no-lookahead)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: principle-no-lookahead
description: "Apply to any evaluation, backtest, benchmark, grader, or training run over data with a time axis or an ordered decision sequence. Score a decision only against inputs that existed before that decision was made. Covers what the field calls look-ahead bias, target leakage, point-in-time correctness, and repainting. Reach for it on: a backtest or walk-forward run, a feature computed over a full history, a retrieval or agent eval, a label assigned after the outcome was known, or any result that looks too good."
disable-model-invocation: true
---

# No lookahead

A result is a result only if the decision behind it could have been made at
the moment it is credited to. Otherwise it is a measurement of hindsight.

The failure has a formal name: **leakage** — "the introduction of information
about the target of a data mining problem which should not be legitimately
available to mine from" (Kaufman et al., see Grounding). In time-series work
it is **look-ahead bias**; the correctness property it violates is
**point-in-time correctness**; the technical-analysis artifact that causes it
is a **repainting** indicator. One defect, four vocabularies.

## The shape

Two artifacts get confused because they are rendered from the same data:

| | Display artifact | Decision artifact |
|---|---|---|
| Answers | what happened | what would I have done |
| May read | the whole history | only rows at or before `t` |
| Revised later | legitimately, yes | never |

A chart, a labelled dataset, an annotated log, a resolved ticket, and a
post-hoc report are all display artifacts. They are drawn *after*, and are
allowed to move a marker back onto the moment the thing "really" happened.
Feed one into a decision evaluation and the decision is credited with
knowledge it did not have.

**The leaked value can be entirely real and the leak still total.** The leak
is in *when the decision became available*, not in the number's accuracy.
A price, a label, or a document can be genuine at time `t` while the
instruction to act on it does not exist until `t + k`.

## Must always

- **Name the cutoff before the first measurement.** For every input, record
  two timestamps: what it is *about*, and when it became *knowable*. Only
  the second one may gate a decision. If a source cannot tell you the
  second, it is a display artifact until proven otherwise.
- **Run the truncation test.** Recompute the decision from only the data
  available at time `t`, and compare it to what the full-history computation
  says at that same `t`. Any disagreement at any `t` is a leak, and the
  disagreeing indices name it. Ten cut points expose a gross leak in
  seconds; a replay at every step is the proof.
- **Separate knowability from actionability.** The truncation test proves the
  input existed at `t`. It does not prove the action was executable at `t`.
  Name the execution cutoff too: the earliest moment the decision could have
  reached the world, and the price, latency, or capacity it would have met
  there. A decision that observes a bar's close and fills at that same close
  is zero-latency, and zero latency is a form of hindsight the prefix test
  cannot see.
- **Bind the test to the artifact that produced the number**, not to a
  library function beside it. A causality test that guards `compute()`
  proves nothing about a script that imports `compute()` and passes it a
  different mode. The gate belongs on the path the reported number came out
  of — including throwaway analysis scripts, notebooks, and heredocs.
- **Treat an implausibly good result as a bug report, not an achievement.**
  Fix the implausibility ceiling from the domain's own known best *before*
  running, and stop on any of: a score above what the best known
  practitioner achieves; an error near zero; a monotone result with no bad
  period across a span long enough to contain one; a metric that improves
  when an input that should be irrelevant is added; a parameter sweep where
  every cell wins. The stronger the number, the earlier you stop. Stop and
  find the mechanism — leakage, an inert axis that never varied the output, a
  frozen constant, or a coding error — before reporting the number at all.
  Report the number of configurations tried alongside the best one; without
  it, a reader cannot tell skill from selection.
- **Re-run the cutoff check after any change to what an input reads** — a
  new normalization, a rolling window, a resample, a join, a smoothing term,
  a re-label. Each can silently widen the window a value is computed over.
- **Reject a negative offset.** Any shift, lag, or window parameter exposed
  to a caller must refuse values that reach forward. An unvalidated
  `shift(k)` is a lookahead switch with no label on it.

## Must never

- Score a decision against a label that was assigned with knowledge of the
  outcome.
- Accept "it only reads past data" from a grep, a variable name, a
  docstring, or a commit message. Causality is a property of the output,
  measured. It is not a property of the source, read.
- Let a display mode and a decision mode share a return type. If one
  function can emit both, a caller will eventually pass the wrong one and
  nothing will say so. Make the display result structurally unable to reach
  the scoring path.
- Report a result whose sign depends on an assumption that was never varied.
  If one unswept constant flips the conclusion, that constant is the finding.
- Publish a number produced before the truncation test ran. On discovery,
  void the old numbers **in place** — move them to a `voided/` path and
  retract the headline — rather than deleting them, so the record shows what
  was believed and for how long.

## The cheap test, in general form

## Grounding

- Leakage, formalized, with the legitimacy-in-time condition and the
  learn-predict separation that avoids it → Shachar Kaufman, Saharon Rosset,
  Claudia Perlich, Ori Stitelman, "Leakage in Data Mining: Formulation,
  Detection, and Avoidance," *ACM TKDD* 6(4), Article 15 (2012)
  <https://doi.org/10.1145/2382577.2382579>. This is the paper that names
  this exact failure; its KDD-Cup and INFORMS case studies are both
  "implausibly good result turned out to be a leak."
- Evaluating only from an origin that moves forward through the data →
  Leonard J. Tashman, "Out-of-sample tests of forecasting accuracy: an
  analysis and review," *International Journal of Forecasting* 16(4),
  437-450 (2000) <https://doi.org/10.1016/S0169-2070(00)00065-0>. Rolling
  origin is the truncation test as a standard evaluation design.
- Why a too-good backtest is evidence about the search process rather than
  the strategy, and why the count of configurations tried must be reported →
  David H. Bailey, Jonathan M. Borwein, Marcos López de Prado, Qiji Jim Zhu,
  "Pseudo-Mathematics and Financial Charlatanism: The Effects of Backtest
  Overfitting on Out-of-Sample Performance," *Notices of the AMS* 61(5),
  458-471 (2014) <https://www.ams.org/notices/201405/rnoti-p458.pdf>.
- Purged cross-validation and embargo, for label overlap leaking across a
  split → Marcos López de Prado, *Advances in Financial Machine Learning*,
  Wiley (2018), ch. 7 and ch. 11.
- "Repainting," for a display artifact that moves a marker backward:
  **no known peer-reviewed prior art.** It is vendor and forum vocabulary
  only. Cite the mechanism, not the word.

Related: [[principle-assert-invariants-not-last-bug]] (the truncation test is
the class invariant, not a patch for the leak you found);
[[principle-explicit-errors]] (a suppressed or unvalidated offset is how the
leak gets in silently); [[principle-report-the-disqualifier]] (what the
report must disclose once the evaluation is clean).

Files in this skill

  • SKILL.md7.6 KB
  • tests/fires_example.md1.3 KB
  • tests/stays_silent_example.md956 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.