feat(cli,ingest): aura data coverage — month-gap inventory for a symbol

aura data coverage <SYMBOL> prints the archive's monthly file-index
coverage: a span line (first..last present month) and either an explicit
'no gaps' line or one 'missing: YYYY-MM..YYYY-MM' line per interior
contiguous hole. The Copper failure mode — a campaign aborting mid-run
because a first/last-bounds check passed while the window start had no
data — is now visible before any run (the real archive reports
'Copper missing: 2017-04..2019-09'). An unknown symbol (no archive
files) refuses on stderr with exit 1; informational absence stays
prose + exit 0 per the verb corpus convention.

Mechanics: list_m1_months goes pub in aura-ingest (archive_extent
deliberately walks past gaps and cannot report them); a new two-level
'aura data' clap namespace follows the Nodes precedent; the archive
root resolves through the normal project path (paths.data override,
data-server default otherwise). Pure report fn with unit tests for the
refusal, the gapless span, and the collapsed-range shape; the headline
e2e runs hostless over a new gapped synthetic-archive fixture.

refs #264 (cut 2, aura data list, follows separately)
This commit is contained in:
2026-07-13 21:49:24 +02:00
parent 28984ba73c
commit 529a8af0d6
2 changed files with 130 additions and 2 deletions
+5 -2
View File
@@ -453,8 +453,11 @@ fn month_end_ms_exclusive(year: u16, month: u8) -> i64 {
/// needs no general symbol-boundary regex and reads no private index (#252:
/// no new data-server surface). Empty when `data_path` doesn't exist or holds
/// no matching file for `symbol` — the same "no archive for this symbol"
/// condition an absent `DataServer` symbol represents.
fn list_m1_months(data_path: &Path, symbol: &str) -> Vec<(u16, u8)> {
/// condition an absent `DataServer` symbol represents. `pub` (#264): unlike
/// [`archive_extent`], which deliberately walks *past* an interior gap to find
/// the window's first/last bar, a coverage report needs the raw month list
/// itself to detect and render that gap.
pub fn list_m1_months(data_path: &Path, symbol: &str) -> Vec<(u16, u8)> {
let prefix = format!("{symbol}_");
let mut months = Vec::new();
let Ok(entries) = std::fs::read_dir(data_path) else {