Reshape the aura data namespace: JSON info, retire coverage #273

Closed
opened 2026-07-14 20:53:40 +02:00 by claude · 2 comments
Collaborator

No aura data verb surfaces a symbol's price geometry — decimals, pip, tick, lot, quote currency — so an agent operating through the CLI alone cannot obtain it headlessly. Two further frictions sit in the same namespace: its output formats diverge (list prints bare text lines, a geometry verb would print JSON), and aura data coverage (#264) has lost its purpose since per-cell fault isolation (#272) landed.

This reshapes the aura data namespace around one principle — every verb emits JSON, no format flag — and resolves all three.

What

aura data info <symbol> — new. Prints a single flat JSON object. It always carries the queried symbol as an identity anchor; every other field is optional geometry read from the <SYMBOL>.meta.json sidecar — description, digits, pipSize, tickSize, lotSize, baseAsset, quoteAsset — each omitted when the sidecar is absent, so a sidecar-less known symbol yields just {"symbol":"…"} rather than a half-success:

$ aura data info FRA40
{"symbol":"FRA40","description":"France 40 Index","digits":1,"pipSize":1,"tickSize":0.1,"lotSize":1,"baseAsset":"F40","quoteAsset":"EUR"}

$ aura data info SYMX          # known to the archive, no geometry sidecar
{"symbol":"SYMX"}

A consumer tests "pipSize" in obj. No provider-specific field escapes — generator, schemaVersion, generatedAtUtc stay internal (parity with the data-server reader's neutrality guarantee).

aura data list — now NDJSON. One JSON value per line instead of a bare name per line, so the whole namespace is JSON while line-oriented composition (| while read, | grep) survives:

$ aura data list
"FRA40"
"US500"

aura data coverage — retired; the rationale is in § Why coverage retires, not merges.

Format: JSON across the namespace, no flag

aura data exists for the headless agent that builds campaigns through the CLI alone (the deployment posture #264 named). For that consumer a format mode is a hazard: the output must not depend on whether a --json flag was set, so info and list emit JSON unconditionally. JSON also makes partiality first-class — the reason info is JSON at all — which a human-text default would forfeit; and one renderer plus its tests is less to carry than two. info is one object; list is NDJSON, because the two verbs share a category (inspection) but not a cardinality (one rich object vs. many flat names) — "both JSON" is not "both the same schema". A terminal reader loses little: compact single-line JSON reads fine, and | jq pretty-prints on demand.

Why coverage retires, not merges

aura data coverage <symbol> printed a symbol's span and interior month gaps — a prophylactic check: know before a run whether the target window is gapless, so the run can be trusted. Per-cell fault isolation (#272) took that job over, and does it better: a run reports CellCoverage{effective_from_ms, effective_to_ms, gap_months[]} for the exact window that matters, at the point it matters, instead of a manual pre-run reconciliation. No information is lost — the span/gap facts live on in the fault report where they bear on a decision; only a redundant verb goes. (Arc-5 in the aura-quadriga research project demonstrated the redundancy: coverage was run to confirm five instruments gapless, then the campaign confirmed the same at exit 0 with no fault blocks — the pre-check was duplicated work.) The span/gaps fields therefore do not migrate into info: info is geometry only.

Error mode

  • symbol unknown (no bars and no sidecar) → refuse with prose, exit 1 (matching the current no archive files found for symbol refusal)
  • symbol known to either source → the JSON with whatever fields exist, exit 0

Cross-repo relationships

  • The reader already exists — Brummel/data-server#3 (closed): added symbol_meta(symbol) -> Option<InstrumentGeometry>, which loads <SYMBOL>.meta.json and returns neutral geometry (nothing provider-specific escapes the API), paired with the existing symbols / has_symbol surface. info is the thin CLI surfacing of that reader — the library side is built.
  • The geometry already resolves internally — #232 (closed): the sidecar's pip reaches the real-data path as geo.pip_size (crates/aura-cli/src/campaign_run.rs:395); the gap here is purely the read-only inspect surface.
  • Complements, does not duplicate, #124 (open): the instrument-reference & cost resolution hierarchy that pulls recorded dataset-metadata in as the middle tier of InstrumentSpec/cost resolution, under the always-winning authored override. That issue resolves geometry into a spec/cost model; this one inspects the raw recorded metadata read-only. #124's premise — "the Pepperstone NFS files today are bars-only … no instrument-structural metadata is carried" (2026-06-22) — was overtaken three days later by the sidecar export and Brummel/data-server#3 (2026-06-25).
  • Introduced the namespace this reshapes — #264 (closed): added aura data list / aura data coverage. This changes list's output shape to NDJSON and retires coverage.
  • Makes coverage redundant — #272 (closed): per-cell fault isolation, whose CellCoverage{effective_from_ms, effective_to_ms, gap_months[]} on the run path subsumes the prophylactic coverage check.
  • Upstream producer — Brummel/cTrader: the MS_SimpleExport cBot (src/MS_SimpleExport/MS_SimpleExport.cs, generator string MS_SimpleExport (cTrader.Automate), schemaVersion 2) writes the sidecars at curate time. (Not Brummel/cTrader-DataExport — despite the apt name, that is an unrelated legacy Delphi export project with no sidecar generation.)

Deliberately out of scope

No provider or resolution field, and no stub for either, though a second data provider and tick-resolution data beside M1 are both foreseen. Those are not additive-field questions — they are symbol-identity questions — and a constant stub ("provider":"Pepperstone") would feign a selectability that does not exist and leak provider identity this verb otherwise withholds. Tracked separately as #274.

Acceptance

  • aura data info <symbol> prints one flat JSON object carrying symbol plus whatever sidecar geometry exists; geometry fields are omitted when the sidecar is absent
  • an unknown symbol (no bars, no sidecar) refuses with prose (no panic, no Debug struct), exit 1
  • no provider-specific field (generator, schemaVersion, generatedAtUtc) reaches the output
  • aura data list emits one JSON value per line (NDJSON)
  • aura data coverage is removed; no span/gaps field appears in info
No `aura data` verb surfaces a symbol's price geometry — decimals, pip, tick, lot, quote currency — so an agent operating through the CLI alone cannot obtain it headlessly. Two further frictions sit in the same namespace: its output formats diverge (`list` prints bare text lines, a geometry verb would print JSON), and `aura data coverage` (#264) has lost its purpose since per-cell fault isolation (#272) landed. This reshapes the `aura data` namespace around one principle — every verb emits JSON, no format flag — and resolves all three. ## What **`aura data info <symbol>` — new.** Prints a single flat JSON object. It always carries the queried `symbol` as an identity anchor; every other field is optional geometry read from the `<SYMBOL>.meta.json` sidecar — `description`, `digits`, `pipSize`, `tickSize`, `lotSize`, `baseAsset`, `quoteAsset` — each omitted when the sidecar is absent, so a sidecar-less known symbol yields just `{"symbol":"…"}` rather than a half-success: ``` $ aura data info FRA40 {"symbol":"FRA40","description":"France 40 Index","digits":1,"pipSize":1,"tickSize":0.1,"lotSize":1,"baseAsset":"F40","quoteAsset":"EUR"} $ aura data info SYMX # known to the archive, no geometry sidecar {"symbol":"SYMX"} ``` A consumer tests `"pipSize" in obj`. No provider-specific field escapes — `generator`, `schemaVersion`, `generatedAtUtc` stay internal (parity with the data-server reader's neutrality guarantee). **`aura data list` — now NDJSON.** One JSON value per line instead of a bare name per line, so the whole namespace is JSON while line-oriented composition (`| while read`, `| grep`) survives: ``` $ aura data list "FRA40" "US500" ``` **`aura data coverage` — retired**; the rationale is in § Why `coverage` retires, not merges. ## Format: JSON across the namespace, no flag `aura data` exists for the headless agent that builds campaigns through the CLI alone (the deployment posture #264 named). For that consumer a format *mode* is a hazard: the output must not depend on whether a `--json` flag was set, so `info` and `list` emit JSON unconditionally. JSON also makes partiality first-class — the reason `info` is JSON at all — which a human-text default would forfeit; and one renderer plus its tests is less to carry than two. `info` is one object; `list` is NDJSON, because the two verbs share a category (inspection) but not a cardinality (one rich object vs. many flat names) — "both JSON" is not "both the same schema". A terminal reader loses little: compact single-line JSON reads fine, and `| jq` pretty-prints on demand. ## Why `coverage` retires, not merges `aura data coverage <symbol>` printed a symbol's span and interior month gaps — a *prophylactic* check: know before a run whether the target window is gapless, so the run can be trusted. Per-cell fault isolation (#272) took that job over, and does it better: a run reports `CellCoverage{effective_from_ms, effective_to_ms, gap_months[]}` for the exact window that matters, at the point it matters, instead of a manual pre-run reconciliation. No information is lost — the span/gap facts live on in the fault report where they bear on a decision; only a redundant verb goes. (Arc-5 in the aura-quadriga research project demonstrated the redundancy: `coverage` was run to confirm five instruments gapless, then the campaign confirmed the same at exit 0 with no fault blocks — the pre-check was duplicated work.) The `span`/`gaps` fields therefore do **not** migrate into `info`: `info` is geometry only. ## Error mode - symbol **unknown** (no bars and no sidecar) → refuse with prose, exit 1 (matching the current `no archive files found for symbol` refusal) - symbol **known** to either source → the JSON with whatever fields exist, exit 0 ## Cross-repo relationships - **The reader already exists — `Brummel/data-server#3`** (closed): added `symbol_meta(symbol) -> Option<InstrumentGeometry>`, which loads `<SYMBOL>.meta.json` and returns *neutral* geometry (nothing provider-specific escapes the API), paired with the existing `symbols` / `has_symbol` surface. `info` is the thin CLI surfacing of that reader — the library side is built. - **The geometry already resolves internally — `#232`** (closed): the sidecar's pip reaches the real-data path as `geo.pip_size` (`crates/aura-cli/src/campaign_run.rs:395`); the gap here is purely the read-only inspect surface. - **Complements, does not duplicate, `#124`** (open): the instrument-reference & cost resolution hierarchy that pulls recorded dataset-metadata in as the middle tier of InstrumentSpec/cost resolution, under the always-winning authored override. That issue *resolves* geometry into a spec/cost model; this one *inspects* the raw recorded metadata read-only. #124's premise — "the Pepperstone NFS files today are bars-only … no instrument-structural metadata is carried" (2026-06-22) — was overtaken three days later by the sidecar export and `Brummel/data-server#3` (2026-06-25). - **Introduced the namespace this reshapes — `#264`** (closed): added `aura data list` / `aura data coverage`. This changes `list`'s output shape to NDJSON and retires `coverage`. - **Makes `coverage` redundant — `#272`** (closed): per-cell fault isolation, whose `CellCoverage{effective_from_ms, effective_to_ms, gap_months[]}` on the run path subsumes the prophylactic coverage check. - **Upstream producer — `Brummel/cTrader`**: the `MS_SimpleExport` cBot (`src/MS_SimpleExport/MS_SimpleExport.cs`, generator string `MS_SimpleExport (cTrader.Automate)`, `schemaVersion 2`) writes the sidecars at curate time. (Not `Brummel/cTrader-DataExport` — despite the apt name, that is an unrelated legacy Delphi export project with no sidecar generation.) ## Deliberately out of scope No `provider` or `resolution` field, and no stub for either, though a second data provider and tick-resolution data beside M1 are both foreseen. Those are not additive-field questions — they are symbol-identity questions — and a constant stub (`"provider":"Pepperstone"`) would feign a selectability that does not exist and leak provider identity this verb otherwise withholds. Tracked separately as #274. ## Acceptance - [ ] `aura data info <symbol>` prints one flat JSON object carrying `symbol` plus whatever sidecar geometry exists; geometry fields are omitted when the sidecar is absent - [ ] an unknown symbol (no bars, no sidecar) refuses with prose (no panic, no Debug struct), exit 1 - [ ] no provider-specific field (`generator`, `schemaVersion`, `generatedAtUtc`) reaches the output - [ ] `aura data list` emits one JSON value per line (NDJSON) - [ ] `aura data coverage` is removed; no `span`/`gaps` field appears in `info`
claude added the feature label 2026-07-14 20:53:40 +02:00
claude changed title from Expose per-symbol instrument geometry through an `aura data info` verb to Reshape the `aura data` namespace: JSON `info`, retire `coverage` 2026-07-15 10:36:17 +02:00
Author
Collaborator

Re-anchoring note after cycle #295 (merged 4ed6455, 2026-07-21), which extracted the member-run recipe out of aura-cli into the new aura-runner and aura-measurement crates and shrank aura-cli to argv/dispatch, argv-to-document translation, and presentation (see crates/aura-vocabulary/tests/c28_layering.rs for the enforced shell allow-list).

This issue's ask is otherwise fully undelivered and unaffected in substance: crates/aura-cli/src/main.rs:1137-1149 still declares enum DataCommand { Coverage(DataCoverageCmd), List } as before, dispatch_data_list (main.rs:2156) still prints one bare symbol name per line (not NDJSON), dispatch_data_coverage (main.rs:2181) is unchanged in behavior, and no aura data info verb exists.

One citation in the issue body is now stale. The '#232 (closed)' bullet points to geo.pip_size at crates/aura-cli/src/campaign_run.rs:395 as evidence the sidecar's pip already reaches the real-data path. That call site moved during #295 part 1 (commit 5c2ac98) and now lives at crates/aura-runner/src/runner.rs:180 (and crates/aura-runner/src/member.rs:154) — campaign_run.rs no longer references geo.pip_size at all. The underlying claim (sidecar geometry already resolves into the real-data path) still holds at the new location; only the file:line needs updating for anyone implementing against it.

When this is implemented, the new aura data info dispatch and any report-shaping helper (analogous to the existing data_list_report / aura_runner::coverage::data_coverage_report split) should follow the #295 layering: thin CLI dispatch in aura-cli, with the reusable report body sourced from aura_ingest::instrument_geometry / DataServer::symbol_meta (crates/aura-ingest/src/lib.rs:411-412) directly, or via an aura-runner helper if a second consumer emerges — consistent with how aura_runner::coverage::interior_gap_months is already the one gap walk shared between the CLI verb and a campaign cell's own coverage annotation.

Re-anchoring note after cycle #295 (merged 4ed6455, 2026-07-21), which extracted the member-run recipe out of aura-cli into the new aura-runner and aura-measurement crates and shrank aura-cli to argv/dispatch, argv-to-document translation, and presentation (see crates/aura-vocabulary/tests/c28_layering.rs for the enforced shell allow-list). This issue's ask is otherwise fully undelivered and unaffected in substance: crates/aura-cli/src/main.rs:1137-1149 still declares `enum DataCommand { Coverage(DataCoverageCmd), List }` as before, `dispatch_data_list` (main.rs:2156) still prints one bare symbol name per line (not NDJSON), `dispatch_data_coverage` (main.rs:2181) is unchanged in behavior, and no `aura data info` verb exists. One citation in the issue body is now stale. The '#232 (closed)' bullet points to `geo.pip_size` at `crates/aura-cli/src/campaign_run.rs:395` as evidence the sidecar's pip already reaches the real-data path. That call site moved during #295 part 1 (commit 5c2ac98) and now lives at `crates/aura-runner/src/runner.rs:180` (and `crates/aura-runner/src/member.rs:154`) — campaign_run.rs no longer references `geo.pip_size` at all. The underlying claim (sidecar geometry already resolves into the real-data path) still holds at the new location; only the file:line needs updating for anyone implementing against it. When this is implemented, the new `aura data info` dispatch and any report-shaping helper (analogous to the existing `data_list_report` / `aura_runner::coverage::data_coverage_report` split) should follow the #295 layering: thin CLI dispatch in aura-cli, with the reusable report body sourced from `aura_ingest::instrument_geometry` / `DataServer::symbol_meta` (crates/aura-ingest/src/lib.rs:411-412) directly, or via an aura-runner helper if a second consumer emerges — consistent with how `aura_runner::coverage::interior_gap_months` is already the one gap walk shared between the CLI verb and a campaign cell's own coverage annotation.
claude self-assigned this 2026-07-25 11:56:55 +02:00
Author
Collaborator

Design minutes (2026-07-25)

Three derived decisions taken while implementing this issue, recorded for audit and veto:

  • description stays out of info for now. The body's example output carries description, but the neutral reader this verb surfaces (data-server's InstrumentGeometry via DataServer::symbol_meta) does not expose it: the schemaVersion-2 sidecar carries the field, the parser drops it. Extending the reader is a change in the data-server repo (tracked there as Brummel/data-server#4), out of this cycle's scope — the body itself names the reader as the contract ("the library side is built"). info therefore ships symbol plus the six reader-exposed fields (digits, pipSize, tickSize, lotSize, baseAsset, quoteAsset); the acceptance criterion is field-agnostic ("symbol plus whatever sidecar geometry exists") and remains satisfied. description joins when the reader exposes it.

  • Unknown-symbol refusal prose. The body asks for a refusal "matching the current no archive files found for symbol refusal" — matched in shape (prose on stderr, exit 1), not in bytes: info's unknown condition is broader than coverage's was (no bars AND no sidecar), so the prose names both absences: no recorded data for symbol "X" (no archive files, no geometry sidecar).

  • Empty archive under NDJSON list. The retired bare-text list printed a no symbols prose line on stdout; under the all-JSON namespace stdout must stay parseable, so an empty archive emits zero stdout lines (the honest NDJSON of an empty set) and the human affordance moves to a stderr notice in the stderr-class vocabulary (#278).

## Design minutes (2026-07-25) Three derived decisions taken while implementing this issue, recorded for audit and veto: - **`description` stays out of `info` for now.** The body's example output carries `description`, but the neutral reader this verb surfaces (`data-server`'s `InstrumentGeometry` via `DataServer::symbol_meta`) does not expose it: the schemaVersion-2 sidecar carries the field, the parser drops it. Extending the reader is a change in the `data-server` repo (tracked there as Brummel/data-server#4), out of this cycle's scope — the body itself names the reader as the contract ("the library side is built"). `info` therefore ships `symbol` plus the six reader-exposed fields (`digits`, `pipSize`, `tickSize`, `lotSize`, `baseAsset`, `quoteAsset`); the acceptance criterion is field-agnostic ("symbol plus whatever sidecar geometry exists") and remains satisfied. `description` joins when the reader exposes it. - **Unknown-symbol refusal prose.** The body asks for a refusal "matching the current *no archive files found for symbol* refusal" — matched in shape (prose on stderr, exit 1), not in bytes: `info`'s unknown condition is broader than coverage's was (no bars AND no sidecar), so the prose names both absences: `no recorded data for symbol "X" (no archive files, no geometry sidecar)`. - **Empty archive under NDJSON `list`.** The retired bare-text `list` printed a `no symbols` prose line on stdout; under the all-JSON namespace stdout must stay parseable, so an empty archive emits zero stdout lines (the honest NDJSON of an empty set) and the human affordance moves to a stderr notice in the stderr-class vocabulary (#278).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#273