Skip to content

Concepts

Temporal safety

Temporal safety answers one question: what data was available when the decision was made? Mostly Right keeps event time, knowledge time, and the decision cutoff separate so you can filter and check historical inputs.

TimeQuestion
Event timeWhen did the measured or forecast event happen?
Knowledge timeWhen could a user first know this input?
Decision timeWhen would the model have acted?

An economic observation can describe March, be released in April, and be revised in May. Those are different facts. Treating the March period as the knowledge time leaks April or May into a March decision.

The temporal toolkit operates on rows that carry knowledge_time:

from datetime import date
from mostlyright import economy
from mostlyright.core import KnowledgeView, TimePoint
series = economy.series(
"cpi",
date(2024, 1, 1),
date(2025, 6, 1),
vintages="all",
)
view = KnowledgeView(
series,
TimePoint("2025-04-01T00:00:00Z"),
)
knowable = view.dataframe()

KnowledgeView validates that knowledge_time exists and is timezone-aware UTC, then returns a defensive copy of rows where knowledge_time <= as_of.

Use assert_no_leakage or LeakageDetector when you want an invalid frame to raise instead of being filtered:

from mostlyright.core.temporal import LeakageDetector, assert_no_leakage

These tools check the timestamp carried by each row. They cannot prove that an upstream timestamp is correct or detect leakage added later in feature engineering.

Not every source knows its exact publication time:

Source timingWhat it supports
ExactFilter and audit against the cutoff
Derived or boundedA conservative approximation
ReconstructedHistorical rows without a strict point-in-time guarantee
Latest-only or unknownNot suitable for strict as-of research by default

Check a source’s timing before making a strong claim about a result.

mostlyright.experimental.align() joins sources against a decision cutoff:

from mostlyright import weather
from mostlyright.experimental import align
target = weather.label.cli("KNYC", "2025-01-01", "2025-01-31")
reports = weather.observations("KNYC")
frame = align(target, reports)

align() is experimental and semver-exempt. Depending on the join and source contract, it filters rows to the cutoff or raises LeakageError. Reconstructed source timestamps cannot support the same point-in-time audit.

weather.training_table() is a compact example built on one predefined recipe:

from mostlyright import weather
example = weather.training_table(
"KNYC",
"2025-01-06",
"2025-01-12",
)

Each row exposes:

  • decision_time_utc: the simulated decision cutoff
  • label_available_time_utc: when the settlement label was available
  • knowable_at_utc: the latest required input time, meaning the earliest instant the complete row could have existed

Use the helper when its target and feature recipe match your research. Its timing guarantee still depends on the quality of the underlying source timestamps.

SDK timing checks are necessary but not sufficient:

  1. Split by time, never randomly.
  2. Recompute rolling and expanding features inside each fold.
  3. Fit preprocessing only on the training portion of each fold.
  4. Group cross-station evaluation by station when claiming geographic generalization.
  5. Filter to the knowable_at_utc cutoff that matches the simulated live decision.

The SDK’s old transform helpers were removed in 2.0. Use pandas or Polars, and fit each transform only on the training rows inside each walk-forward fold.

  • Bucketing observations by raw UTC date instead of local_standard_date
  • Selecting the final revised economy value instead of the first vintage
  • Using a forecast run issued after the simulated decision
  • Computing a rolling feature once over the full dataset before splitting
  • Treating label availability as though it were feature availability