Skip to content

mostlyright.core.temporal.leakage

LeakageDetector — loud assertion of as-of leakage absence.

Where KnowledgeView silently filters, assert_no_leakage raises LeakageError when one or more rows have knowledge_time > as_of. The error payload follows the design.md §D contract: it carries the count of violating rows and a sample (capped at 10) so callers can surface the problem without dumping the entire offending DataFrame.

Used by:

  • The audit path of research() Mode 2 dispatch (loud failure).
  • Validator’s optional leakage check (when caller asks for it).
  • Test fixtures verifying KnowledgeView’s filter behaviour.

See docs/design.md §M (leakage semantics) + §D (LeakageError payload).

assert_issued_at_populated(df)Raise IssuedAtMissingError if any row has null issued_at.
assert_no_leakage(df, as_of)Raise LeakageError if any row’s knowledge_time > as_of.
LeakageDetector(as_of)Convenience wrapper for repeated detection against a fixed as_of.

class mostlyright.core.temporal.leakage.LeakageDetector(as_of)

Section titled “class mostlyright.core.temporal.leakage.LeakageDetector(as_of)”

Bases: object

Convenience wrapper for repeated detection against a fixed as_of.

Run assert_no_leakage() against the bound as_of.

Accepts either a raw DataFrame or a MostlyRightResult wrapper (unwrapped inside assert_no_leakage()).

Raise IssuedAtMissingError if any row has null issued_at.

Phase 20 OM-04 extension. Independent of as_of — the bound cutoff is irrelevant when the row carries no model-run time at all.

mostlyright.core.temporal.leakage.assert_issued_at_populated(df)

Section titled “mostlyright.core.temporal.leakage.assert_issued_at_populated(df)”

Raise IssuedAtMissingError if any row has null issued_at.

Forecast rows MUST carry their model-run time to be leakage-safe; a missing issued_at means we cannot verify the cycle predated the as_of cutoff in research(). For Open-Meteo this should be impossible by construction (the fetcher derives issued_at per row), so this check is a defensive net.

Mirrors structural conventions of assert_no_leakage(): MostlyRightResult (and any duck-typed .df carrier) unwrap, column-existence guard, sample-cap.

Phase 20 OM-04.

mostlyright.core.temporal.leakage.assert_no_leakage(df, as_of)

Section titled “mostlyright.core.temporal.leakage.assert_no_leakage(df, as_of)”

Raise LeakageError if any row’s knowledge_time > as_of.

Phase 6 W0-T6: accepts either a raw DataFrame or a MostlyRightResult; wrapped polars frames are converted to pandas via MostlyRightResult.frame_as_pandas() because the body of this function uses pd.Timestamp + iterrows semantics.

  • Parameters:
    • df (DataFrame | MostlyRightResult) – DataFrame with a tz-aware UTC knowledge_time column, OR a MostlyRightResult wrapping such a frame.
    • as_of (TimePoint) – The as-of cutoff. Rows with strictly greater knowledge_time count as leakage.
  • Raises:
    • SchemaValidationError – if knowledge_time is missing or not tz-aware UTC.
    • TypeError – if as_of is not a TimePoint.
    • LeakageError – if ≥1 rows have knowledge_time > as_of. Payload carries violating_count and a sample (≤10) of violations.
  • Return type: None

A leak-free frame passes silently:

>>> import pandas as pd
>>> from mostlyright.core import TimePoint, assert_no_leakage
>>> df = pd.DataFrame({
... "knowledge_time": pd.to_datetime(["2025-01-01T00:00:00Z"], utc=True),
... "value": [10],
... })
>>> assert_no_leakage(df, TimePoint("2025-01-02T00:00:00Z"))

A row past the cutoff raises LeakageError:

>>> from mostlyright.core import LeakageError
>>> leaky = pd.DataFrame({
... "knowledge_time": pd.to_datetime(["2025-01-03T00:00:00Z"], utc=True),
... "value": [99],
... })
>>> try:
... assert_no_leakage(leaky, TimePoint("2025-01-02T00:00:00Z"))
... except LeakageError as err:
... print(err.violating_count)
1