From 1605205fc2512285cf5037c3e31765eefcc1a61f Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 12 Jul 2026 18:51:41 +0200 Subject: [PATCH] fix(cli): empty --real window refuses naming the window, not the symbol probe_window's two empty-result exits now route through a distinct no_data_in_window refusal (requested bounds + archive path); its callers have already proven the symbol present via has_symbol, so the old "no local data for symbol" reuse misattributed a window fact to symbol absence. The genuinely-unknown-symbol message and exit codes stay. closes #242 --- crates/aura-cli/src/main.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 68166fb..6e6c41b 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -564,6 +564,22 @@ fn no_real_data(symbol: &str, env: &project::Env) -> ! { std::process::exit(1) } +/// Empty-in-window refusal — stderr + exit(1). Distinct from `no_real_data`: +/// used only inside `probe_window`, whose callers have already proven the +/// symbol present via `has_symbol` — an empty probe result there is a fact +/// about the requested `--from`/`--to` window, not about symbol absence, so +/// it must not reuse the symbol-absence message (#242). +fn no_data_in_window(symbol: &str, from_ms: Option, to_ms: Option, env: &project::Env) -> ! { + let bound = |b: Option| b.map_or_else(|| "unbounded".to_string(), |v| v.to_string()); + eprintln!( + "aura: no data for symbol '{symbol}' in the requested window [{}, {}] at {}", + bound(from_ms), + bound(to_ms), + env.data_path() + ); + std::process::exit(1) +} + /// Resolve the per-instrument pip from the recorded geometry sidecar, or refuse /// (stderr + exit 1) when the symbol has no recorded geometry — the single home of /// the guessed-pip refusal, shared by `open_real_source` and `from_choice`. Reads @@ -588,8 +604,10 @@ fn pip_or_refuse( /// Probe the full data window: open a single-pass probe source through the /// shared opener, drain it for the first/last timestamp, and return -/// `(first, last)`. Refuses (via `no_real_data`) when the symbol/window yields -/// no source or no bars. Shared by `open_real_source` (which needs the manifest +/// `(first, last)`. Refuses (via `no_data_in_window`) when the symbol/window +/// yields no source or no bars — callers have already proven the symbol +/// present, so an empty result here is a window fact, not symbol absence. +/// Shared by `open_real_source` (which needs the manifest /// window from a probe separate from the run sources) and /// `DataSource::full_window`. The probe drains the CLOSE column regardless of /// the strategy's binding: the bounds are field-independent (every archived bar @@ -603,10 +621,11 @@ fn probe_window( env: &project::Env, ) -> (Timestamp, Timestamp) { let mut probe = aura_ingest::open_columns(server, symbol, from_ms, to_ms, &[aura_ingest::M1Field::Close]) - .unwrap_or_else(|| no_real_data(symbol, env)) + .unwrap_or_else(|| no_data_in_window(symbol, from_ms, to_ms, env)) .pop() .expect("open_columns yields one source per requested field"); - let first = aura_engine::Source::peek(probe.as_ref()).unwrap_or_else(|| no_real_data(symbol, env)); + let first = + aura_engine::Source::peek(probe.as_ref()).unwrap_or_else(|| no_data_in_window(symbol, from_ms, to_ms, env)); let mut last = first; while let Some((t, _)) = aura_engine::Source::next(&mut *probe) { last = t;