From ac4a8c68cfd9a2d074be5d1d49c90232e6f0c79d Mon Sep 17 00:00:00 2001 From: Brummel Date: Thu, 4 Jun 2026 23:44:56 +0200 Subject: [PATCH] docs(aura-ingest): precise None contract for load_m1_window The rustdoc claimed 'None if the symbol has no data in [from_ms, to_ms]', implying a bar-level None. Empirically (cycle-0011 fieldtest) None is file-level: it propagates data-server's own None, returned only when no archived file overlaps the window (far-future window or unknown symbol). A window that overlaps a loaded file but holds zero bars returns Some(M1Columns) with empty columns, not None. Document the distinction precisely (chosen over collapsing in-coverage- empty into None): None propagates data-server's one file-level meaning unchanged -- 'no data source to read from' -- rather than overloading it with a second aura-level 'source present but empty here' meaning. So Some/None means 'data source present' vs 'nothing to read from', not 'has bars' vs 'has none'; a consumer testing for an empty window must check cols.close.is_empty(). Doc-only, no behaviour change; ledger untouched (the contract is not referenced there). closes #18 --- crates/aura-ingest/src/lib.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/aura-ingest/src/lib.rs b/crates/aura-ingest/src/lib.rs index 7134e3c..f5d178e 100644 --- a/crates/aura-ingest/src/lib.rs +++ b/crates/aura-ingest/src/lib.rs @@ -87,16 +87,25 @@ pub fn transpose_m1(bars: &[M1Parsed]) -> M1Columns { /// Drain a data-server M1 window into transposed SoA columns. Reads the /// chronological chunk iterator to exhaustion into one AoS buffer, then /// transposes once at the boundary (C3 — one merge point, not per chunk). -/// `None` if the symbol has no data in the window (data-server's own `None`); -/// the inclusive Unix-ms window bounds are data-server's contract. /// -/// Each bound is an `Option`: `None` means unbounded on that side -/// (data-server skips the coarse `unix_ms_to_year_month` file filter rather -/// than reaching for a sentinel). `from_ms = None` reads from the start of -/// history; `to_ms = None` reads to the end. This threads data-server's own -/// per-bound `Option` contract through unchanged, so "to the end of history" -/// is expressible without an `i64::MAX` sentinel (which panics upstream in -/// chrono's `from_timestamp_millis`). +/// Each bound is an `Option` of inclusive Unix-ms (data-server's window +/// contract): `None` means unbounded on that side (data-server skips the +/// coarse `unix_ms_to_year_month` file filter rather than reaching for a +/// sentinel). `from_ms = None` reads from the start of history; `to_ms = None` +/// reads to the end. This threads data-server's own per-bound `Option` +/// contract through unchanged, so "to the end of history" is expressible +/// without an `i64::MAX` sentinel (which panics upstream in chrono's +/// `from_timestamp_millis`). +/// +/// The returned `Option` is **file-level**, propagating data-server's own +/// `None`: it is `None` only when no archived file overlaps the window (e.g. a +/// far-future window, or an unknown symbol) — i.e. there is no data source to +/// read from. A window that *does* overlap a loaded file but happens to hold +/// zero bars (a narrow gap inside coverage) returns `Some(M1Columns)` with +/// empty columns, **not** `None`. So `Some`/`None` distinguishes "data source +/// present" from "nothing to read from", not "has bars" from "has none": a +/// consumer testing for "no bars in this window" must check +/// `cols.close.is_empty()`, not the `Option` alone. pub fn load_m1_window( server: &Arc, symbol: &str,