feat(0099): CLI exit-code split (iteration 2) — usage=2, runtime=1
Apply the clean exit-code partition (#175 deviation #8) on top of the clap migration: runtime failures now exit 1, usage errors stay exit 2, with no same-class inconsistency. Partition (attribution principle): exit 2 = a command-line fault (a bad flag value, an inapplicable combination, or the content of an argv-named file); exit 1 = the command was well-formed but the environment / recorded state it needs is missing, or piped stdin data is bad. The 6 boundary cases the spec's "parse-time vs run-time" rule under-specified are resolved on #175 (malformed/open argv blueprint -> usage 2; stdin op-script content -> runtime 1; applicability refusals -> usage 2; unknown --axis / --metric -> usage 2). Flipped 2->1 (runtime): no local data, no recorded geometry, no recorded run/family, "run has no tap named", trace-name collision, family/trace persist-write failures (normalizing the lone generalize inconsistency -- all persist -> 1 together), missing/corrupt content-addressed store state (the reproduce path), chart-read failures, and graph stdin op-script content + stdin-read I/O. reproduce-diverged stays exit 1. Usage sites (clap parse, argv-applicability guards, the dual-grammar blueprint-file read/parse) stay exit 2. Tests: ~8 domain-refusal pins flipped Some(2)->Some(1) (message substrings unchanged), the four runtime tests renamed _exit_2 -> _exit_1, and a partition property test added. Orchestrator fix after the loop: 3 sibling no-data skip-guards (run_real_with_trace + the two generalize cross-instrument tests) that the plan's Task-1 list missed -- left at Some(2), they would fall through to assert Some(0) and FAIL on a data-less machine; flipped to Some(1) for consistency with the flipped code. Deferred (cosmetic, filed forward): full "Usage:" / "usage:" / bare error-message casing normalization -- no functional impact, high pin-churn. Verified green (orchestrator, this session): cargo build --workspace, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace all clean (0 failed across every test binary). This completes the #175 clap-adoption cycle (iteration 1 = clap migration at 366170a; iteration 2 = this exit-code split). refs #175
This commit is contained in:
@@ -129,7 +129,7 @@ pub fn build_cmd() {
|
||||
let mut doc = String::new();
|
||||
if let Err(e) = std::io::stdin().read_to_string(&mut doc) {
|
||||
eprintln!("aura: reading stdin: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
match build_from_str(&doc) {
|
||||
// `print!`, not `println!`: the canonical artifact is exactly the library
|
||||
@@ -139,7 +139,7 @@ pub fn build_cmd() {
|
||||
Ok(json) => print!("{json}"),
|
||||
Err(msg) => {
|
||||
eprintln!("aura: {msg}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,13 +209,13 @@ pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd) {
|
||||
let mut doc = String::new();
|
||||
if let Err(e) = std::io::stdin().read_to_string(&mut doc) {
|
||||
eprintln!("aura: reading stdin: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
match introspect_unwired(&doc) {
|
||||
Ok(s) => print!("{s}"),
|
||||
Err(m) => {
|
||||
eprintln!("aura: {m}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -227,13 +227,13 @@ pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd) {
|
||||
let mut doc = String::new();
|
||||
if let Err(e) = std::io::stdin().read_to_string(&mut doc) {
|
||||
eprintln!("aura: reading stdin: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
match build_from_str(&doc) {
|
||||
Ok(json) => println!("{}", crate::content_id(&json)),
|
||||
Err(m) => {
|
||||
eprintln!("aura: {m}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-45
@@ -187,7 +187,7 @@ fn sim_optimal_manifest(
|
||||
/// (beside the run registry's `runs/`). Shared by every run form — all drain the same
|
||||
/// two f64 taps (equity off the broker, exposure off the strategy) and carry a
|
||||
/// `RunManifest`. Pure wiring over `ColumnarTrace` + `TraceStore`; the engine is
|
||||
/// untouched. An I/O failure is a usage-level error (stderr + exit 2), per the spec.
|
||||
/// untouched. An I/O failure is a runtime error (stderr + exit 1), per the spec.
|
||||
fn persist_traces(
|
||||
name: &str,
|
||||
manifest: &RunManifest,
|
||||
@@ -200,7 +200,7 @@ fn persist_traces(
|
||||
];
|
||||
if let Err(e) = TraceStore::open("runs").write(name, manifest, &taps) {
|
||||
eprintln!("aura: trace persist failed: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,8 +497,8 @@ fn filter_to_tap(data: ChartData, tap: &str) -> Result<ChartData, String> {
|
||||
|
||||
/// `aura chart <name> [--tap <t>] [--panels]`: classify the name and render. A
|
||||
/// single run charts all its taps (or the one `--tap` selects); a family overlays
|
||||
/// one tap (default `equity`) across its members; an unknown name is a usage error
|
||||
/// (stderr + exit 2), never a panic.
|
||||
/// one tap (default `equity`) across its members; an unknown name is a runtime error
|
||||
/// (stderr + exit 1), never a panic.
|
||||
fn emit_chart(name: &str, tap: Option<&str>, mode: ChartMode) {
|
||||
let store = TraceStore::open("runs");
|
||||
match store.name_kind(name) {
|
||||
@@ -507,7 +507,7 @@ fn emit_chart(name: &str, tap: Option<&str>, mode: ChartMode) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let mut data = build_chart_data(name, traces);
|
||||
@@ -516,7 +516,7 @@ fn emit_chart(name: &str, tap: Option<&str>, mode: ChartMode) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -528,14 +528,14 @@ fn emit_chart(name: &str, tap: Option<&str>, mode: ChartMode) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let data = match build_comparison_chart_data(name, &members, tap.unwrap_or("equity")) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let data = decimate(data, CHART_DECIMATE_BUCKETS);
|
||||
@@ -546,7 +546,7 @@ fn emit_chart(name: &str, tap: Option<&str>, mode: ChartMode) {
|
||||
"aura: no recorded run or family '{name}' under runs/traces \
|
||||
(run `aura run --trace {name}` or `aura sweep --trace {name}` first)"
|
||||
);
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,7 +559,7 @@ fn run_sample(trace: Option<&str>) -> RunReport {
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(n, WriteKind::Run)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let (mut h, rx_eq, rx_ex) = sample_harness(SYNTHETIC_PIP_SIZE);
|
||||
let sources: Vec<Box<dyn aura_engine::Source>> =
|
||||
@@ -592,13 +592,13 @@ fn run_sample(trace: Option<&str>) -> RunReport {
|
||||
/// fold as `run_sample`. The manifest window is read from a *separate* probe source
|
||||
/// (a Source is single-pass), so the run source streams the window untouched.
|
||||
/// A no-local-data condition (unknown symbol, or a window overlapping no file / no
|
||||
/// bars) is a usage error: stderr + exit(2), not a panic.
|
||||
/// bars) is a runtime error: stderr + exit(1), not a panic.
|
||||
fn run_sample_real(symbol: &str, from_ms: Option<i64>, to_ms: Option<i64>, trace: Option<&str>) -> RunReport {
|
||||
if let Some(n) = trace
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(n, WriteKind::Run)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let (source, window, pip_size) = open_real_source(symbol, from_ms, to_ms);
|
||||
let (mut h, rx_eq, rx_ex) = sample_harness(pip_size);
|
||||
@@ -655,14 +655,14 @@ enum DataSource {
|
||||
},
|
||||
}
|
||||
|
||||
/// No-local-data refusal — stderr + exit(2), mirroring `run_sample_real`.
|
||||
/// No-local-data refusal — stderr + exit(1), mirroring `run_sample_real`.
|
||||
fn no_real_data(symbol: &str) -> ! {
|
||||
eprintln!("aura: no local data for symbol '{symbol}' at {}", data_server::DEFAULT_DATA_PATH);
|
||||
std::process::exit(2)
|
||||
std::process::exit(1)
|
||||
}
|
||||
|
||||
/// Resolve the per-instrument pip from the recorded geometry sidecar, or refuse
|
||||
/// (stderr + exit 2) when the symbol has no recorded geometry — the single home of
|
||||
/// (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
|
||||
/// the symbol's geometry metadata (not bar data) before the run source opens, so an
|
||||
/// instrument with no recorded geometry refuses without a guessed pip; the pip is
|
||||
@@ -676,7 +676,7 @@ fn pip_or_refuse(server: &std::sync::Arc<data_server::DataServer>, symbol: &str)
|
||||
refusing to run a real instrument with a guessed pip",
|
||||
data_server::DEFAULT_DATA_PATH
|
||||
);
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -706,7 +706,7 @@ fn probe_window(
|
||||
/// the run source paired with its manifest `window` and per-instrument `pip_size`.
|
||||
/// Single home of the real-source construction the single-run handlers share — the
|
||||
/// sidecar-pip lookup, the `DataServer` `has_symbol` refusal, the probe-window pass, and
|
||||
/// the run-source `open` (each refusal an stderr + exit 2). Pre-data refusals keep the
|
||||
/// the run-source `open` (each refusal an stderr + exit 1). Pre-data refusals keep the
|
||||
/// pip honest by construction. Shared by `run_sample_real` and `run_stage1_r`.
|
||||
fn open_real_source(
|
||||
symbol: &str,
|
||||
@@ -731,7 +731,7 @@ fn open_real_source(
|
||||
}
|
||||
|
||||
impl DataSource {
|
||||
/// Build a provider from a parsed choice, or refuse (stderr + exit 2) on a symbol
|
||||
/// Build a provider from a parsed choice, or refuse (stderr + exit 1) on a symbol
|
||||
/// with no recorded geometry / absent data — both BEFORE any member runs (Fork C/G),
|
||||
/// via the same `pip_or_refuse` / `no_real_data` helpers as
|
||||
/// `run_sample_real`.
|
||||
@@ -1613,7 +1613,7 @@ fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, gr
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(name, WriteKind::Family)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let reg = default_registry();
|
||||
let family = match strategy {
|
||||
@@ -1627,7 +1627,7 @@ fn run_sweep(strategy: Strategy, name: &str, persist: bool, data: DataSource, gr
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
for pt in &family.points {
|
||||
@@ -1690,7 +1690,7 @@ fn run_walkforward(strategy: Strategy, name: &str, persist: bool, data: DataSour
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(name, WriteKind::Family)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let reg = default_registry();
|
||||
let result = walkforward_family(strategy, persist.then_some(name), &data, grid, select);
|
||||
@@ -1700,7 +1700,7 @@ fn run_walkforward(strategy: Strategy, name: &str, persist: bool, data: DataSour
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
for w in &result.windows {
|
||||
@@ -2009,7 +2009,7 @@ fn run_mc(name: &str, persist: bool) {
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(name, WriteKind::Family)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let reg = default_registry();
|
||||
let family = mc_family(persist.then_some(name));
|
||||
@@ -2017,7 +2017,7 @@ fn run_mc(name: &str, persist: bool) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
for draw in &family.draws {
|
||||
@@ -2102,7 +2102,7 @@ fn runs_families() {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
for fam in group_families(members) {
|
||||
@@ -2122,7 +2122,7 @@ fn runs_family(id: &str, rank: Option<&str>) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
let Some(family) = group_families(members).into_iter().find(|f| f.id == id) else {
|
||||
@@ -2184,11 +2184,11 @@ fn point_from_params(space: &[ParamSpec], params: &[(String, Scalar)]) -> Vec<Ce
|
||||
.map(|(_, s)| s.cell())
|
||||
.unwrap_or_else(|| {
|
||||
// A manifest missing a param the reloaded space expects is
|
||||
// corrupted-on-disk data — exit cleanly (`aura:` + exit 2) like
|
||||
// corrupted-on-disk data — exit cleanly (`aura:` + exit 1) like
|
||||
// every other persisted-data failure on the reproduce path, not
|
||||
// a panic.
|
||||
eprintln!("aura: manifest is missing param {}", ps.name);
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
@@ -2199,13 +2199,13 @@ fn point_from_params(space: &[ParamSpec], params: &[(String, Scalar)]) -> Vec<Ce
|
||||
fn reproduce_family_in(reg: &Registry, id: &str, data: &DataSource) -> ReproduceReport {
|
||||
let members = reg.load_family_members().unwrap_or_else(|e| {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
});
|
||||
let Some(family) = group_families(members).into_iter().find(|f| f.id == id) else {
|
||||
// reproduce is an action, not a lookup: an unknown id is a hard error (distinct
|
||||
// from `runs family <id>`'s treat-as-empty exit 0).
|
||||
eprintln!("aura: no such family '{id}'");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
};
|
||||
let pip = data.pip_size();
|
||||
let window = data.full_window();
|
||||
@@ -2214,17 +2214,17 @@ fn reproduce_family_in(reg: &Registry, id: &str, data: &DataSource) -> Reproduce
|
||||
let stored = &member.report;
|
||||
let hash = stored.manifest.topology_hash.clone().unwrap_or_else(|| {
|
||||
eprintln!("aura: family member has no topology_hash; not a generated run");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
});
|
||||
let doc = reg
|
||||
.get_blueprint(&hash)
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
eprintln!("aura: blueprint {hash} missing from store");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
});
|
||||
// Reload the stored blueprint per use: a Composite is !Clone, and both the
|
||||
// param-space probe (below) and the re-run each consume one. The doc was
|
||||
@@ -2232,7 +2232,7 @@ fn reproduce_family_in(reg: &Registry, id: &str, data: &DataSource) -> Reproduce
|
||||
let reload = || {
|
||||
blueprint_from_json(&doc, &|t| std_vocabulary(t)).unwrap_or_else(|e| {
|
||||
eprintln!("aura: stored blueprint {hash} does not parse: {e:?}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
})
|
||||
};
|
||||
// The param_space of the WRAPPED signal — its knobs carry the `stage1_r`
|
||||
@@ -2401,7 +2401,7 @@ fn run_macd(trace: Option<&str>) -> RunReport {
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(n, WriteKind::Run)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let (tx_eq, rx_eq) = mpsc::channel();
|
||||
let (tx_ex, rx_ex) = mpsc::channel();
|
||||
@@ -3107,14 +3107,14 @@ fn run_blueprint_sweep(doc: &str, axes: &[(String, Vec<Scalar>)], name: &str, pe
|
||||
.expect("a loaded blueprint re-serializes");
|
||||
reg.put_blueprint(&topo, &canonical).unwrap_or_else(|e| {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
});
|
||||
// Record the family unconditionally (C18/C21 lineage), exactly like `run_sweep`.
|
||||
let id = match reg.append_family(name, FamilyKind::Sweep, &sweep_member_reports(&family)) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
for pt in &family.points {
|
||||
@@ -3141,10 +3141,10 @@ fn run_blueprint_walkforward(doc: &str, axes: &[(String, Vec<Scalar>)], name: &s
|
||||
&blueprint_from_json(doc, &|t| std_vocabulary(t)).expect("doc parse-validated at the dispatch boundary"),
|
||||
)
|
||||
.expect("a loaded blueprint re-serializes");
|
||||
reg.put_blueprint(&topo, &canonical).unwrap_or_else(|e| { eprintln!("aura: {e}"); std::process::exit(2); });
|
||||
reg.put_blueprint(&topo, &canonical).unwrap_or_else(|e| { eprintln!("aura: {e}"); std::process::exit(1); });
|
||||
let id = match reg.append_family(name, FamilyKind::WalkForward, &walkforward_member_reports(&result)) {
|
||||
Ok(id) => id,
|
||||
Err(e) => { eprintln!("aura: {e}"); std::process::exit(2); }
|
||||
Err(e) => { eprintln!("aura: {e}"); std::process::exit(1); }
|
||||
};
|
||||
for w in &result.windows {
|
||||
println!("{}", family_member_line(&id, &w.run.oos_report));
|
||||
@@ -3177,13 +3177,13 @@ fn run_blueprint_mc(doc: &str, n_seeds: u64, name: &str, data: DataSource) {
|
||||
.expect("a loaded blueprint re-serializes");
|
||||
reg.put_blueprint(&topo, &canonical).unwrap_or_else(|e| {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
});
|
||||
let id = match reg.append_family(name, FamilyKind::MonteCarlo, &mc_member_reports(&family)) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
for draw in &family.draws {
|
||||
@@ -3422,7 +3422,7 @@ fn run_stage1_r(
|
||||
&& let Err(e) = TraceStore::open("runs").ensure_name_free(n, WriteKind::Run)
|
||||
{
|
||||
eprintln!("aura: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let (tx_eq, rx_eq) = mpsc::channel();
|
||||
let (tx_ex, rx_ex) = mpsc::channel();
|
||||
@@ -3521,7 +3521,7 @@ fn persist_traces_r(
|
||||
}
|
||||
if let Err(e) = TraceStore::open("runs").write(name, manifest, &taps) {
|
||||
eprintln!("aura: trace persist failed: {e}");
|
||||
std::process::exit(2);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3596,7 +3596,7 @@ fn run_dispatch(args: RunArgs) -> Result<RunReport, String> {
|
||||
// `parse_param_cells`). The four dual-grammar subcommands carry an optional
|
||||
// `[blueprint]` positional; a first-positional that names an existing `.json` file
|
||||
// (`is_blueprint_file`) selects the loaded-blueprint branch, otherwise the built-in
|
||||
// grammar. Exit codes are behaviour-preserving (usage errors stay exit 2).
|
||||
// grammar. Usage errors (clap parse + argv-applicability guards) exit 2; runtime failures exit 1.
|
||||
|
||||
/// The `aura` root parser. `#[command(version)]` reads `CARGO_PKG_VERSION`
|
||||
/// (the workspace `0.1.0`), so `aura --version` prints `aura 0.1.0`.
|
||||
@@ -5095,7 +5095,7 @@ mod tests {
|
||||
/// local Pepperstone archive is absent, so the test never fails on a machine
|
||||
/// without the data. Uses `GER40` — a *vetted* symbol (pip 1.0): the
|
||||
/// per-instrument-pip refusal makes `run_sample_real` reject an un-specced
|
||||
/// symbol before any data access (`std::process::exit(2)`), so this CLI-level
|
||||
/// symbol before any data access (`std::process::exit(1)`), so this CLI-level
|
||||
/// test must drive a symbol in the instrument table. The bounded-window AAPL.US
|
||||
/// streaming property still lives in the ingest `streaming_seam` test, which
|
||||
/// builds its source literally without the spec lookup.
|
||||
|
||||
Reference in New Issue
Block a user