feat(0074): source real-path pip from the geometry sidecar; drop the authored floor

The data-server now ships per-symbol geometry sidecars, so aura no longer
hard-codes instrument geometry. A consumption grep showed production reads
exactly one `InstrumentSpec` field — `pip_size` — at the real-data path; the
other eight (incl. `instrument_id`) were constructed but never read.

- aura-cli resolves the real-path pip from the recorded sidecar
  (`instrument_geometry` -> `InstrumentGeometry.pip_size`), refusing (exit 2)
  on absent geometry. The authored table no longer gates: any symbol with a
  sidecar + bars now runs (new test: USDJPY, never vetted, resolves its JPY
  pip 0.01 from the sidecar).
- Removed the redundant authored floor: `InstrumentSpec`, `instrument_spec`,
  `VETTED_SYMBOLS`, and the table-only tests. The `InstrumentGeometry` seam
  (the surviving pip source) and its nameability guard are kept.
- `instrument_id` (the "find out"): no production consumer. The position-event
  table's `instrument_id` is a decoupled caller parameter; the engine never
  imports an instrument spec. Dropped with the struct.
- The example `pip_size_of` is re-sourced to the sidecar; the pure-structural
  blueprint tests use a literal pip to stay archive-independent.

Speculative deploy-grade fields and the #124 override/floor tiers are deferred
to the Stage-2 broker that will consume them, read from the sidecar geometry
then. Ledger (C8/C15/#22/#106) updated; #124 collapses to "recorded sidecar
geometry -> refuse" in the meantime.

Verified: cargo test --workspace (all green), clippy -D warnings clean, cargo
doc clean, with the local archive present so the sidecar paths run.

refs #124
This commit is contained in:
2026-06-25 19:06:12 +02:00
parent 4fd047adf3
commit 7e4b91ab5f
11 changed files with 198 additions and 464 deletions
+28 -22
View File
@@ -38,10 +38,10 @@ use std::collections::HashSet;
/// The pip size the built-in *synthetic* harnesses run at: a 5-decimal FX major
/// (`EURUSD`-shaped). The synthetic streams carry no instrument, so there is no
/// `instrument_spec` to thread; a single named source keeps the broker's divisor
/// (`sample_harness` / `SimBroker::builder`) and the recorded broker label
/// recorded geometry sidecar to thread; a single named source keeps the broker's
/// divisor (`sample_harness` / `SimBroker::builder`) and the recorded broker label
/// (`sim_optimal_manifest`) in lockstep, so they cannot silently drift apart.
/// The real path threads the looked-up `spec.pip_size` instead.
/// The real path threads the sidecar's looked-up `pip_size` instead.
const SYNTHETIC_PIP_SIZE: f64 = 0.0001;
/// Real walk-forward roller sizes (Fork D/F). `WindowRoller` takes sizes in the
@@ -723,15 +723,21 @@ fn no_real_data(symbol: &str) -> ! {
std::process::exit(2)
}
/// Look up the vetted per-instrument pip/spec, or refuse (stderr + exit 2) on an
/// un-specced symbol — the single home of the guessed-pip refusal message, shared
/// by `run_sample_real` and `DataSource::from_choice`. Refusing BEFORE any data
/// access keeps the pip honest by construction (never guessed).
fn instrument_spec_or_refuse(symbol: &str) -> aura_ingest::InstrumentSpec {
match aura_ingest::instrument_spec(symbol) {
Some(s) => s,
/// 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
/// 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
/// the provider's recorded value, honest by construction.
fn pip_or_refuse(server: &std::sync::Arc<data_server::DataServer>, symbol: &str) -> f64 {
match aura_ingest::instrument_geometry(server, symbol) {
Some(geo) => geo.pip_size,
None => {
eprintln!("aura: no vetted pip/instrument spec for symbol '{symbol}' — refusing to run a real instrument with a guessed pip (add it to the instrument table)");
eprintln!(
"aura: no recorded geometry for symbol '{symbol}' at {}\
refusing to run a real instrument with a guessed pip",
data_server::DEFAULT_DATA_PATH
);
std::process::exit(2);
}
}
@@ -758,10 +764,10 @@ fn probe_window(
(first, last)
}
/// Open a real M1-close source for a vetted symbol over an optional window, returning
/// Open a real M1-close source for a recorded symbol over an optional window, returning
/// 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
/// vetted-pip lookup, the `DataServer` `has_symbol` refusal, the probe-window pass, and
/// 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
/// pip honest by construction. Shared by `run_sample_real` and `run_stage1_r`.
fn open_real_source(
@@ -769,10 +775,10 @@ fn open_real_source(
from_ms: Option<i64>,
to_ms: Option<i64>,
) -> (Box<dyn aura_engine::Source>, (Timestamp, Timestamp), f64) {
// Per-instrument pip: look up BEFORE any data access, so an un-specced symbol
// refuses without touching local data. Honest by construction.
let spec = instrument_spec_or_refuse(symbol);
// Per-instrument pip from the recorded sidecar; resolved BEFORE bar-data access
// so an instrument with no geometry refuses without touching the archive.
let server = std::sync::Arc::new(data_server::DataServer::new(data_server::DEFAULT_DATA_PATH));
let pip = pip_or_refuse(&server, symbol);
if !server.has_symbol(symbol) {
no_real_data(symbol);
}
@@ -783,24 +789,24 @@ fn open_real_source(
Some(s) => Box::new(s),
None => no_real_data(symbol),
};
(source, window, spec.pip_size)
(source, window, pip)
}
impl DataSource {
/// Build a provider from a parsed choice, or refuse (stderr + exit 2) on an
/// un-vetted symbol / absent data — both BEFORE any member runs (Fork C/G),
/// via the same `instrument_spec_or_refuse` / `no_real_data` helpers as
/// Build a provider from a parsed choice, or refuse (stderr + exit 2) 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`.
fn from_choice(choice: DataChoice) -> DataSource {
match choice {
DataChoice::Synthetic => DataSource::Synthetic,
DataChoice::Real { symbol, from_ms, to_ms } => {
let spec = instrument_spec_or_refuse(&symbol);
let server = std::sync::Arc::new(data_server::DataServer::new(data_server::DEFAULT_DATA_PATH));
let pip = pip_or_refuse(&server, &symbol);
if !server.has_symbol(&symbol) {
no_real_data(&symbol);
}
DataSource::Real { server, symbol, from_ms, to_ms, pip: spec.pip_size }
DataSource::Real { server, symbol, from_ms, to_ms, pip }
}
}
}