Tracker sweep + executor robustness: net-R chain, archive inventory, std::grid, per-cell fault isolation (12 issues) #270
@@ -2740,6 +2740,8 @@ struct DataCmd {
|
||||
enum DataCommand {
|
||||
/// Report a symbol's monthly file-index coverage (span + interior gaps).
|
||||
Coverage(DataCoverageCmd),
|
||||
/// List the archive's known symbols, sorted, one per line.
|
||||
List,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -3674,9 +3676,37 @@ fn dispatch_nodes_new(a: NodesNewCmd) {
|
||||
fn dispatch_data(cmd: DataCmd, env: &project::Env) {
|
||||
match cmd.command {
|
||||
DataCommand::Coverage(a) => dispatch_data_coverage(a, env),
|
||||
DataCommand::List => dispatch_data_list(env),
|
||||
}
|
||||
}
|
||||
|
||||
/// `aura data list` (#264 cut 2): print the archive's known symbols, sorted
|
||||
/// ascending, one per line — the discovery step before `aura data coverage`
|
||||
/// or scoping a campaign's instrument matrix. Resolves the archive root
|
||||
/// through the normal project path ([`dispatch_data_coverage`]'s
|
||||
/// `Env::data_path`), then reuses `DataServer`'s own symbol index
|
||||
/// (`DataServer::symbols()`, already sorted) rather than re-deriving a
|
||||
/// directory scan. An empty or absent archive is informational absence, not a
|
||||
/// fault: a `no symbols` prose line, exit 0 (the verb corpus's established
|
||||
/// empty-result register — cf. `runs family`'s unknown-id empty exit 0).
|
||||
fn dispatch_data_list(env: &project::Env) {
|
||||
let data_path = std::path::PathBuf::from(env.data_path());
|
||||
let server = aura_ingest::DataServer::new(&data_path);
|
||||
for line in data_list_report(&server.symbols()) {
|
||||
println!("{line}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Pure: render `symbols` (already sorted by `DataServer::symbols()`) as one
|
||||
/// line per symbol, or a single `no symbols` line when the archive is empty
|
||||
/// or absent — informational absence, never a fault (#264).
|
||||
fn data_list_report(symbols: &[std::sync::Arc<str>]) -> Vec<String> {
|
||||
if symbols.is_empty() {
|
||||
return vec!["no symbols".to_string()];
|
||||
}
|
||||
symbols.iter().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
/// `aura data coverage <SYMBOL>` (#264 cut 1): print the archive's month
|
||||
/// coverage for `SYMBOL` — the Copper failure mode (files present at both
|
||||
/// ends of a window while an interior month is missing, so a first/last-bounds
|
||||
@@ -4203,6 +4233,26 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// An empty (or absent) archive is informational absence, not a fault
|
||||
/// (#264): `data_list_report` returns the single `no symbols` prose line
|
||||
/// rather than an empty `Vec`, so the caller's exit-0 println always has
|
||||
/// something to say.
|
||||
#[test]
|
||||
fn data_list_report_of_an_empty_archive_is_a_no_symbols_line() {
|
||||
let symbols: Vec<std::sync::Arc<str>> = vec![];
|
||||
assert_eq!(data_list_report(&symbols), vec!["no symbols".to_string()]);
|
||||
}
|
||||
|
||||
/// A non-empty archive renders one line per symbol, in the order handed
|
||||
/// in (`DataServer::symbols()` already sorts) — no `no symbols` line
|
||||
/// mixed in (#264).
|
||||
#[test]
|
||||
fn data_list_report_of_a_populated_archive_is_one_line_per_symbol() {
|
||||
let symbols: Vec<std::sync::Arc<str>> =
|
||||
vec![std::sync::Arc::from("SYMA"), std::sync::Arc::from("SYMB")];
|
||||
assert_eq!(data_list_report(&symbols), vec!["SYMA".to_string(), "SYMB".to_string()]);
|
||||
}
|
||||
|
||||
/// Disjoint archives (no shared instant across all listed symbols) refuse
|
||||
/// rather than silently pooling a floor over different periods per
|
||||
/// instrument (#213); the error names each symbol next to its own span,
|
||||
|
||||
Reference in New Issue
Block a user