Consume DataServer instrument geometry to build and cross-check the vetted InstrumentSpec table #143

Closed
opened 2026-06-25 13:59:07 +02:00 by Brummel · 2 comments
Owner

The provider metadata that #124 anticipated as its recorded dataset-metadata
tier
(tier 2 of the resolution hierarchy) now physically exists. The data
path's export now writes one per-symbol geometry sidecar <SYMBOL>.meta.json
beside the bars — verified: 30 files in /mnt/tickdata/Pepperstone, all
schemaVersion 2 (digits, pipSize, tickSize, lotSize, baseAsset,
quoteAsset). #124's Mechanics section assumed "the Pepperstone NFS files today
are bars-only ... no instrument-structural metadata is carried" — that premise is
now superseded.

This issue is the concrete consumer realization of that tier at the
aura-ingest edge, gated on the upstream reader in data-server issue
http://192.168.178.103:3000/Brummel/data-server/issues/3, which parses the
sidecars into neutral InstrumentGeometry. aura-ingest consumes that neutral
geometry — never the raw cTrader JSON — so the broker-agnostic core is preserved.

It does not re-decide #124's precedence hierarchy or the quote_currency
type fork (a distinct ResolvedInstrumentSpec vs. widening to Cow) raised in
#124's comment thread; those remain #124's to settle. This is the wiring and
the two field extensions the sidecars now enable.

Work

  1. Tier-2 wiring. aura-ingest consumes the neutral InstrumentGeometry
    from the data-server reader and slots it as tier 2 under #124's precedence:
    explicit authored override > recorded dataset metadata (this) > vetted
    instrument_spec() floor (#113) > refuse-don't-guess (exit 2). The raw
    JSON never enters this repo.

  2. Cross-check arm (high value, low cost). The sidecar's lotSize * pipSize
    reproduces the invariant pip_value_per_lot = contract_size * pip_size that
    InstrumentSpec already asserts and tests (crates/aura-ingest/src/lib.rs,
    the C10 contract — pip_value_per_lot comment + the test at ~lib.rs:474–486).
    A build/test-time cross-check of the hand-authored vetted table against
    provider geometry is one multiplication and catches a typo in the table
    against broker truth. Directly relevant to the hand-registration in #140
    (NAS100, USDJPY pip 0.01, USDCHF, NZDUSD): the sidecars carry, verified,
    USDJPY pip 0.01 / lot 100000, NAS100 pip 1 / lot 1, USDCHF &
    NZDUSD pip 0.0001 / lot 100000.

  3. Two new fields. Add tick_size and digits to InstrumentSpec — the two
    fields it deliberately deferred (current floor is six fields:
    instrument_id, pip_size, contract_size, pip_value_per_lot, min_lot,
    lot_step, quote_currency). A provider source now supplies them; they are
    the deploy-grade price/tick rounding inputs.

  4. quote_currency beyond FX. The field is currently FX-only and typed
    &'static str. The sidecars carry clean ISO quote currency for every asset
    class (indices EUR/HKD/JPY/GBP, metals USD). This runtime-sourced
    currency is exactly the case #124's comment flagged the &'static str floor
    cannot hold — so this consumes that fork's outcome rather than pre-empting it.

Invariants preserved

  • Refuse, don't guess. An unknown symbol still exits 2; the sidecar feeds
    the authoring/cross-check of the vetted floor, never a silent runtime fallback.
  • Untrusted input. The sidecar self-stamps its generator and can emit
    null for non-finite broker values; it is an input the authored override
    supersedes (the spread case in #124), not trusted truth.
  • Engine stays domain-free. Geometry is non-scalar reference metadata held
    beside the hot path (C15), keyed by symbol — never a streamed scalar. The
    Source seam keeps carrying only (Timestamp, Scalar)
    (crates/aura-engine/src/harness.rs); data-server enters only through
    aura-ingest (M1FieldSource), not the engine core.

Field mapping (verified against the 30 sidecars)

sidecar InstrumentSpec
pipSize pip_size
lotSize contract_size
lotSize * pipSize pip_value_per_lot (in quoteAsset)
quoteAsset quote_currency (extends past FX)
digits, tickSize new fields
min_lot, lot_step (provider doc: current order limits, not geometry — stay authored)
instrument_id (a registry choice — stays aura's, C18)

References

  • depends on: http://192.168.178.103:3000/Brummel/data-server/issues/3
    (upstream neutral-geometry reader)
  • relates to: #124 (resolution hierarchy and the tier-2 home; this realizes its
    tier 2), #113 (the authored vetted floor), #71 (DataServer / Source seam),
    #140 (the hand-registration the cross-check covers)
The provider metadata that `#124` anticipated as its **recorded dataset-metadata tier** (tier 2 of the resolution hierarchy) now physically exists. The data path's export now writes one per-symbol geometry sidecar `<SYMBOL>.meta.json` beside the bars — verified: 30 files in `/mnt/tickdata/Pepperstone`, all `schemaVersion` 2 (`digits`, `pipSize`, `tickSize`, `lotSize`, `baseAsset`, `quoteAsset`). `#124`'s Mechanics section assumed "the Pepperstone NFS files today are bars-only ... no instrument-structural metadata is carried" — that premise is now superseded. This issue is the concrete **consumer** realization of that tier at the `aura-ingest` edge, gated on the upstream reader in data-server issue `http://192.168.178.103:3000/Brummel/data-server/issues/3`, which parses the sidecars into neutral `InstrumentGeometry`. `aura-ingest` consumes that neutral geometry — never the raw cTrader JSON — so the broker-agnostic core is preserved. It does **not** re-decide `#124`'s precedence hierarchy or the `quote_currency` type fork (a distinct `ResolvedInstrumentSpec` vs. widening to `Cow`) raised in `#124`'s comment thread; those remain `#124`'s to settle. This is the wiring and the two field extensions the sidecars now enable. ## Work 1. **Tier-2 wiring.** `aura-ingest` consumes the neutral `InstrumentGeometry` from the data-server reader and slots it as tier 2 under `#124`'s precedence: explicit authored override > recorded dataset metadata (this) > vetted `instrument_spec()` floor (`#113`) > refuse-don't-guess (`exit 2`). The raw JSON never enters this repo. 2. **Cross-check arm (high value, low cost).** The sidecar's `lotSize * pipSize` reproduces the invariant `pip_value_per_lot = contract_size * pip_size` that `InstrumentSpec` already asserts and tests (`crates/aura-ingest/src/lib.rs`, the C10 contract — `pip_value_per_lot` comment + the test at ~lib.rs:474–486). A build/test-time cross-check of the hand-authored vetted table against provider geometry is one multiplication and catches a typo in the table against broker truth. Directly relevant to the hand-registration in `#140` (NAS100, USDJPY pip `0.01`, USDCHF, NZDUSD): the sidecars carry, verified, `USDJPY` pip `0.01` / lot `100000`, `NAS100` pip `1` / lot `1`, `USDCHF` & `NZDUSD` pip `0.0001` / lot `100000`. 3. **Two new fields.** Add `tick_size` and `digits` to `InstrumentSpec` — the two fields it deliberately deferred (current floor is six fields: `instrument_id`, `pip_size`, `contract_size`, `pip_value_per_lot`, `min_lot`, `lot_step`, `quote_currency`). A provider source now supplies them; they are the deploy-grade price/tick rounding inputs. 4. **`quote_currency` beyond FX.** The field is currently FX-only and typed `&'static str`. The sidecars carry clean ISO quote currency for every asset class (indices `EUR`/`HKD`/`JPY`/`GBP`, metals `USD`). This runtime-sourced currency is exactly the case `#124`'s comment flagged the `&'static str` floor cannot hold — so this consumes that fork's outcome rather than pre-empting it. ## Invariants preserved - **Refuse, don't guess.** An unknown symbol still exits `2`; the sidecar feeds the authoring/cross-check of the vetted floor, never a silent runtime fallback. - **Untrusted input.** The sidecar self-stamps its `generator` and can emit `null` for non-finite broker values; it is an input the authored override supersedes (the spread case in `#124`), not trusted truth. - **Engine stays domain-free.** Geometry is non-scalar reference metadata held beside the hot path (C15), keyed by symbol — never a streamed scalar. The `Source` seam keeps carrying only `(Timestamp, Scalar)` (`crates/aura-engine/src/harness.rs`); `data-server` enters only through `aura-ingest` (`M1FieldSource`), not the engine core. ## Field mapping (verified against the 30 sidecars) | sidecar | → `InstrumentSpec` | |---|---| | `pipSize` | `pip_size` | | `lotSize` | `contract_size` | | `lotSize * pipSize` | `pip_value_per_lot` (in `quoteAsset`) | | `quoteAsset` | `quote_currency` (extends past FX) | | `digits`, `tickSize` | new fields | | — | `min_lot`, `lot_step` (provider doc: current order limits, not geometry — stay authored) | | — | `instrument_id` (a registry choice — stays aura's, C18) | ## References - depends on: `http://192.168.178.103:3000/Brummel/data-server/issues/3` (upstream neutral-geometry reader) - relates to: `#124` (resolution hierarchy and the tier-2 home; this realizes its tier 2), `#113` (the authored vetted floor), `#71` (DataServer / Source seam), `#140` (the hand-registration the cross-check covers)
Brummel added the feature label 2026-06-25 13:59:07 +02:00
Author
Owner

Design reconciliation (specify) — cycle 0073

Spec: docs/specs/0073-instrument-geometry-crosscheck.md. The fork below is a
derived orchestrator decision on an autonomous /boss run (not a minuted
user decision); it carries its rationale and is vetoable.

  • Fork: scope boundary — how much of #143 lands now vs. defers to #124.
    #143 delivers geometry consumption + cross-check + two additive fields;
    it builds no runtime resolver and does not change
    InstrumentSpec.quote_currency's type.

    Basis: derived. This issue's body is internally in tension — its preamble
    twice defers #124's precedence resolver and the quote_currency type-fork
    (ResolvedInstrumentSpec vs. Cow widening) to #124, while work-items 1 and 4
    read as if a runtime resolver and a widened currency field were in scope here.
    The deferral language is authoritative, and the code confirms it: there is no
    resolver or broker consumer yet (#116 / #120–123 open, unimplemented), and a
    four-tier resolver producing a runtime currency is exactly #124's reserved
    type-fork. So the internally-consistent reading — and the title's own framing,
    "consume … to build and cross-check the vetted InstrumentSpec table" — is:

    1. Tier-2 source access at the aura-ingest edge: re-export
      InstrumentGeometry + a thin accessor over DataServer::symbol_meta,
      mirroring the existing pub use data_server::{DataServer, DEFAULT_DATA_PATH}
      discipline so a consumer builds from aura-ingest alone and never names
      data_server. The geometry data becomes available; the tier-composing
      resolver stays #124.
    2. Two additive InstrumentSpec fields tick_size: f64, digits: u32
      (keeps Copy, C15-additive), authored for the 5 vetted symbols from the
      verified sidecars (GER40/FRA40: digits 1, tick 0.1; EURUSD/GBPUSD/USDCAD:
      digits 5, tick 1e-5).
    3. Build/test-time cross-check: each vetted symbol's authored floor vs.
      provider geometry (pip_size, contract_size == lot_size,
      pip_value_per_lot == lot_size * pip_size, tick_size, digits),
      iterating the vetted set so #140's future additions are covered for free;
      skip-if-archive-absent per the existing real-data test pattern.
    4. "quote_currency beyond FX" is satisfied at the geometry tier
      (InstrumentGeometry.quote: String carries every asset class), not by
      mutating InstrumentSpec's field type — that transition is #124's.

    The Cargo.lock bump to the data-server commit carrying the reader (#3,
    closed) is part of this cycle.

Impact on related issues

  • #124 — its tier-2 data is now reachable at the edge; the precedence
    resolver, the quote_currency type-fork, and the spread/commission tier-2
    channel all remain #124's (the sidecar is geometry-only, no cost models).
  • #140 — the cross-check iterates the vetted set, so registering NAS100 /
    USDJPY (pip 0.01) / USDCHF / NZDUSD there will be validated against provider
    geometry for free.
  • #113 — the authored floor is extended additively (C15) and now validated
    against broker truth.
  • #71 — geometry stays non-scalar reference metadata beside the hot path
    (C15); the Source seam still carries only (Timestamp, Scalar) — the accessor
    is not a Source.
## Design reconciliation (specify) — cycle 0073 Spec: `docs/specs/0073-instrument-geometry-crosscheck.md`. The fork below is a **derived** orchestrator decision on an autonomous `/boss` run (not a minuted user decision); it carries its rationale and is vetoable. - **Fork: scope boundary — how much of #143 lands now vs. defers to #124.** → #143 delivers *geometry consumption + cross-check + two additive fields*; it builds **no** runtime resolver and does **not** change `InstrumentSpec.quote_currency`'s type. Basis: derived. This issue's body is internally in tension — its preamble twice defers #124's precedence resolver and the `quote_currency` type-fork (ResolvedInstrumentSpec vs. Cow widening) to #124, while work-items 1 and 4 read as if a runtime resolver and a widened currency field were in scope here. The deferral language is authoritative, and the code confirms it: there is no resolver or broker consumer yet (#116 / #120–123 open, unimplemented), and a four-tier resolver producing a runtime currency is exactly #124's reserved type-fork. So the internally-consistent reading — and the title's own framing, "consume … to **build and cross-check** the vetted InstrumentSpec table" — is: 1. **Tier-2 source access** at the aura-ingest edge: re-export `InstrumentGeometry` + a thin accessor over `DataServer::symbol_meta`, mirroring the existing `pub use data_server::{DataServer, DEFAULT_DATA_PATH}` discipline so a consumer builds from `aura-ingest` alone and never names `data_server`. The geometry *data* becomes available; the tier-composing resolver stays #124. 2. **Two additive `InstrumentSpec` fields** `tick_size: f64`, `digits: u32` (keeps `Copy`, C15-additive), authored for the 5 vetted symbols from the verified sidecars (GER40/FRA40: digits 1, tick 0.1; EURUSD/GBPUSD/USDCAD: digits 5, tick 1e-5). 3. **Build/test-time cross-check**: each vetted symbol's authored floor vs. provider geometry (`pip_size`, `contract_size == lot_size`, `pip_value_per_lot == lot_size * pip_size`, `tick_size`, `digits`), iterating the vetted set so #140's future additions are covered for free; skip-if-archive-absent per the existing real-data test pattern. 4. "quote_currency beyond FX" is satisfied at the **geometry tier** (`InstrumentGeometry.quote: String` carries every asset class), not by mutating `InstrumentSpec`'s field type — that transition is #124's. The `Cargo.lock` bump to the data-server commit carrying the reader (#3, closed) is part of this cycle. ### Impact on related issues - **#124** — its tier-2 *data* is now reachable at the edge; the precedence resolver, the `quote_currency` type-fork, and the spread/commission tier-2 channel all remain #124's (the sidecar is geometry-only, no cost models). - **#140** — the cross-check iterates the vetted set, so registering NAS100 / USDJPY (pip 0.01) / USDCHF / NZDUSD there will be validated against provider geometry for free. - **#113** — the authored floor is extended additively (C15) and now validated against broker truth. - **#71** — geometry stays non-scalar reference metadata beside the hot path (C15); the Source seam still carries only `(Timestamp, Scalar)` — the accessor is **not** a `Source`.
Author
Owner

Done — cycle 0073 (autonomous /boss run)

Shipped (local commits above the pushed anchor fceba07, unpushed — the user pushes):
8d99397 spec (boss-signed) · d5e9c27 data-server lock-bump to the geometry-reader commit · 5b7cd5e feat · dda20ab audit/close.

What landed

  1. Tier-2 source access at the aura-ingest edge: pub use data_server::meta::InstrumentGeometry + a thin instrument_geometry(server, symbol) -> Option<InstrumentGeometry> accessor over DataServer::symbol_meta. A consumer builds from aura-ingest alone; the raw provider JSON never enters this repo.
  2. Two additive InstrumentSpec fields tick_size: f64, digits: u32 (struct stays Copy; C15-additive), authored for the 5 vetted symbols from the verified sidecars.
  3. Cross-check (tests/instrument_geometry_crosscheck.rs): the authored floor vs provider geometry — pip_size, contract_size == lot_size, the C10 pip_value_per_lot == lot_size * pip_size invariant, tick_size, digits, quote_currency — iterating VETTED_SYMBOLS, skip-if-archive-absent.

Extras (reviewed, kept): a hoisted pub const VETTED_SYMBOLS collapsing the prior duplicated lists (so #140 extends one constant); a tick_size == 10^(-digits) consistency guard (verified to hold across all 30 provider sidecars, so it will not false-trip #140); a re-export-nameable seam test.

Scope held (per the reconciliation comment above): no runtime resolver, and quote_currency is still &'static str — both remain #124's. "quote_currency beyond FX" is satisfied at the geometry tier (InstrumentGeometry.quote: String).

Verification: cargo test --workspace all green; cargo clippy --workspace --all-targets -- -D warnings clean. The cross-check ran against the live Pepperstone archive (not skipped) — all 5 vetted sidecars match the authored table. Durable rationale lifted into the C15 realization note in docs/design/INDEX.md; the cycle spec/plan dropped per the lifecycle convention.

Closing. closes #143.

## Done — cycle 0073 (autonomous /boss run) Shipped (local commits above the pushed anchor `fceba07`, **unpushed** — the user pushes): `8d99397` spec (boss-signed) · `d5e9c27` data-server lock-bump to the geometry-reader commit · `5b7cd5e` feat · `dda20ab` audit/close. **What landed** 1. **Tier-2 source access** at the aura-ingest edge: `pub use data_server::meta::InstrumentGeometry` + a thin `instrument_geometry(server, symbol) -> Option<InstrumentGeometry>` accessor over `DataServer::symbol_meta`. A consumer builds from `aura-ingest` alone; the raw provider JSON never enters this repo. 2. **Two additive `InstrumentSpec` fields** `tick_size: f64`, `digits: u32` (struct stays `Copy`; C15-additive), authored for the 5 vetted symbols from the verified sidecars. 3. **Cross-check** (`tests/instrument_geometry_crosscheck.rs`): the authored floor vs provider geometry — `pip_size`, `contract_size == lot_size`, the C10 `pip_value_per_lot == lot_size * pip_size` invariant, `tick_size`, `digits`, `quote_currency` — iterating `VETTED_SYMBOLS`, skip-if-archive-absent. Extras (reviewed, kept): a hoisted `pub const VETTED_SYMBOLS` collapsing the prior duplicated lists (so #140 extends one constant); a `tick_size == 10^(-digits)` consistency guard (verified to hold across **all 30** provider sidecars, so it will not false-trip #140); a re-export-nameable seam test. **Scope held** (per the reconciliation comment above): no runtime resolver, and `quote_currency` is still `&'static str` — both remain #124's. "quote_currency beyond FX" is satisfied at the geometry tier (`InstrumentGeometry.quote: String`). **Verification**: `cargo test --workspace` all green; `cargo clippy --workspace --all-targets -- -D warnings` clean. The cross-check ran against the live Pepperstone archive (not skipped) — all 5 vetted sidecars match the authored table. Durable rationale lifted into the C15 realization note in `docs/design/INDEX.md`; the cycle spec/plan dropped per the lifecycle convention. Closing. `closes #143`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#143