Commit Graph

378 Commits

Author SHA1 Message Date
Brummel b91c89f964 feat(broker-foundation): InstrumentSpec deploy metadata + PositionEvent schema
The realistic-broker milestone's (C10 A-side) two foundation value types — no
broker, no derivation yet, fully tested.

#113 — InstrumentSpec gains six deploy-grade fields (instrument_id,
contract_size, pip_value_per_lot, min_lot, lot_step, quote_currency) and the
vetted table is populated for GER40/FRA40/EURUSD/GBPUSD/USDCAD. The struct stays
Copy (quote_currency is &'static str); the _ => None refuse-don't-guess arm is the
permanent floor. This is the milestone's permanent authored floor (recorded-metadata
tier is forward work, #124).

#114 — PositionAction { Buy, Sell, Close } (closed enum, serde-encoded as a bare
i64 0/1/2 via From/TryFrom) + PositionEvent in aura-engine beside RunMetrics,
faithful to the C10 column spec: event_ts, action, position_id, instrument_id,
unsigned volume; no open_ts; direction is the action (no signed-volume trick).
Re-exported from the crate root.

Derived fork decisions (recorded on #113): quote_currency &'static str; action as
ordinal i64; stable instrument_id + overridable vetted values.

Verified by the orchestrator: cargo test --workspace = 458 passed / 0 failed;
clippy --all-targets -D warnings clean. An adversarial 4-lens review (ledger
faithfulness, downstream soundness, serde/wire robustness, test honesty) returned
3 SOUND + 1 CONCERN: quote_currency &'static str cannot hold a runtime-sourced
currency at the #124 resolver — not a blocker (#115/#116 read only the authored
floor), logged on #124 as a deliberate deferred design choice. The implementer's
deviation from the plan snippet (vec![..] -> [..]) avoids clippy::useless_vec;
semantics identical.

Two tester-authored consumer-boundary E2E tests added beyond the inline units:
instrument_spec_deploy_metadata.rs (public resolution seam) and
position_event_table.rs (persisted bare-int wire shape + reversal table).

closes #113 #114
2026-06-22 19:51:17 +02:00
Brummel 099aac87e1 plan: 0063 broker-foundation — InstrumentSpec deploy metadata + PositionEvent schema
Three tasks. Task 1 (#113, compile-atomic): RED-rewrite the lookup test to the
six-field shape + invariant/uniqueness tests, then extend InstrumentSpec and
populate the vetted table for the five instruments (refuse arm stays). Task 2
(#114, greenfield): RED the round-trip / try_from-Err / serde-bare-int / reversal
tests, then add PositionAction (serde-as-i64) + PositionEvent in report.rs and
re-export from the crate root. Task 3: workspace test + clippy gate.

Recon-grounded line anchors; the redundant std::convert::TryFrom import dropped
(2021 prelude). INDEX.md realization-note drift (C10/C15 "minimal today") deferred
to the cycle-close audit, not folded here (code-only scope).

refs #113 #114
2026-06-22 19:22:42 +02:00
Brummel cc0dce19a3 spec: 0063 broker-foundation — InstrumentSpec deploy metadata + PositionEvent schema (boss-signed)
The broker milestone's (C10 A-side) foundation: two pure value-type
deliverables, no broker and no derivation yet.

- #113: extend InstrumentSpec with deploy-grade fields (instrument_id,
  contract_size, pip_value_per_lot, min_lot, lot_step, quote_currency) +
  populate the vetted table for the five instruments; refuse-don't-guess
  arm stays. Permanent authored floor of the override hierarchy (#124).
- #114: PositionAction { Buy, Sell, Close } (closed enum, serde-encoded as
  i64) + PositionEvent row in aura-engine, faithful to the C10 column spec
  (no open_ts, unsigned volume, direction is the action).

Signed under /boss on a grounding-check PASS (all current-state assumptions
ratified by green tests; the two new types are greenfield, expected).
Derived fork decisions (quote_currency &'static str; action enum encoded as
i64 0/1/2; stable instrument_id + overridable vetted values) recorded on #113.

refs #113 #114
2026-06-22 19:14:07 +02:00
Brummel 3638d4822e docs(ledger): record the tap-aware decimation refinement (#111) in the cut-2 note
The cut-2 amendment described decimate as a pure min/max transform; #111 made the
per-bucket reduction tap-aware (mean for the bounded exposure, min/max for equity).
Keep the always-loaded design memory accurate; note #112 (bars/OHLC rendering) and
#110 as the remaining deferred refinements.

refs #111 #112
2026-06-22 14:35:10 +02:00
Brummel 2957561c30 fix(chart): mean-decimate the bounded exposure series instead of a min/max band
The serve-time decimation (#108) reduced every series by per-bucket min/max. That is
the right envelope for a cumulative equity curve but wrong for the bounded exposure
stream (C10, f64 ∈ [-1,+1]): over a multi-year window each ~hundreds-of-bars bucket
straddles sign flips, so min/max is ±1 in nearly every bucket and the exposure
collapses to a solid -1..+1 band that reads as per-point oscillation — even for a
calm strategy (a 50/200 member flips only 0.81% of bars yet still bands out).

Make decimation tap-aware (RED-first, #111): a new `ReduceKind {MinMax, Mean}` on
each Series, set by the chart builders via `reduce_for_tap` (exposure -> Mean, else
MinMax). `decimate` honours it — a Mean series emits the per-bucket mean (its
net/duty-cycle level) in both spine slots, a MinMax series keeps min/max. The shared
spine, the equity rendering, and the page payload are unchanged (reduce is
server-side only, `#[serde(skip)]`). Verified on real GER40 1y M1: the served
exposure series goes from a ±1 band to a smooth net-level line in [-0.38, +0.34].

Rendering the min/max envelope honestly (range bars / OHLC, vs today's polyline) is
the deferred other half — filed as #112.

Verified: cargo test --workspace = 447 passed / 0 failed (incl. the RED-then-GREEN
decimate_mean_reduces_a_bipolar_series_to_its_bucket_level); clippy -D warnings clean.

closes #111
refs #112
2026-06-22 14:34:21 +02:00
Brummel e01bdc3d5e audit(visual-world-cut-2): cycle close 0062 — ledger records cut 2 (#102 + #108)
Cycle-close tidy for visual-world-cut-2 (decimation #108 + run-context header #102).

Architect drift review (53eeba5..HEAD): the only drift was a STALE LEDGER — the C22
section still listed #102 (run-context header) and #108 (decimation) as deferred/open
after they shipped in 476342d. Resolved in this commit:
- the #101 first-cut note no longer claims "not yet a run-context header";
- a new "served-page hardening — cut 2" amendment records #102 (ChartMeta +
  buildHeader, family window = span across members) and #108 (the pure decimate
  min-max transform) as landed, both CLI-side (C14);
- the "Open architectural threads" list drops the now-landed run-context header.

Substantive gates (self-verified): C14 honoured — all decimation/header/pixel logic
is in aura-cli (render.rs/main.rs/chart-viewer.js); the only engine touch in range is
the one-line #100 cell.rs doc. C1 preserved — decimate is pure, deterministic
bucketing over the sorted-deduped spine, no-op under budget, monotonic spine (pinned
by a unit test). Regression: cargo test --workspace = 446 passed / 0 failed; cargo
clippy --workspace --all-targets -D warnings clean. No separate baseline script.

Residual debt filed as #110 (idea): the decimation budget is a fixed const (no
--width flag), and the min-then-max sub-pixel ordering is a real ts-displacement in
the non-default continuous x-mode. Both are local aura-cli refinements, neither a bug.

Ephemeral spec/plan removed at cycle close (docs/specs/0062, docs/plans/0062).

Cycle 0062 tidy: drift-clean after this ledger amendment.
2026-06-22 12:42:38 +02:00
Brummel 476342d7b1 feat(chart): decimate served pages + run-context header (visual World cut 2)
Hardens the families-comparison chart page (#107) so it is openable and legible on
real multi-year data. Two halves, both confined to the CLI render path (engine +
registry untouched, C14):

#108 — serve-time min-max decimation. A pure `decimate(ChartData, buckets)`
transform partitions the shared union-ts spine into <=~2000 buckets and emits each
series' per-bucket min+max (nulls preserved), bounding the served page to a few
thousand points regardless of the underlying M1 point count. Applied in emit_chart
on both the single-run and family paths; a no-op under budget (every existing
fixture). Full recorded data stays on disk; only the page is thinned. For a
walk-forward family the null-fill blow-up collapses for free (an all-null bucket
stays null). Deterministic (C1).

#102 — a run-context header. A `ChartMeta` (name/commit/window/broker/seed/taps,
+ member count for a family, + bound params for a single run) is built from the
RunManifest in build_chart_data / build_comparison_chart_data (which gain a `name`
arg), injected into window.AURA_TRACES.meta, and rendered by a new pure buildHeader
in chart-viewer.js (headless .mjs-guarded). The family window is the SPAN across
members (min from, max to), not the first member's window — the only reading that
labels a disjoint walk-forward family's true OOS coverage (it collapses to the
shared window for sweep/MC). Reuses the existing render_value for param display
(no new Scalar Display, keeping C7's tag-only param plane). The earlier
"manifest is NOT carried into the page" render pin is flipped to a positive one.

Verified: cargo test --workspace = 446 passed / 0 failed; cargo clippy --workspace
--all-targets -D warnings clean. New coverage: 5 decimate unit tests, single-run +
family meta wiring (incl. the span-window invariant), a buildHeader headless guard,
and two end-to-end cli_run tests pinning the served-page meta at the binary boundary.

closes #108
closes #102
2026-06-22 12:37:09 +02:00
Brummel 12bfa50a4f plan: 0062 visual-world-cut-2 — decimation + run-context header
Three tasks on the aura-cli render path + a workspace gate:
- Task 1 (#102, compile-atomic): ChartMeta + ChartData.meta, built from RunManifest
  in build_chart_data/build_comparison_chart_data (gain a `name` param), every call
  site threaded (emit_chart + 3 comparison unit tests + the render test helper), the
  manifest re-injected into window.AURA_TRACES, the negative render pin flipped.
- Task 2 (#102): #ctx CHART_HEAD slot + a pure buildHeader in chart-viewer.js with a
  node-driven headless guard pair (chart_viewer_header.{mjs,rs}).
- Task 3 (#108): a pure decimate(ChartData, buckets) min-max transform applied in
  emit_chart on both paths; engine + registry untouched (C14).
- Task 4: cargo test --workspace + clippy -D warnings.

refs #108
refs #102
2026-06-22 11:48:46 +02:00
Brummel a8e11e7733 spec: 0062 visual-world-cut-2 — decimation + run-context header (boss-signed)
Served-page hardening for the families-comparison chart, bundling two issues that
share one code locus (the aura-cli render path) and one purpose (C22/C14: make the
#107 view openable + legible on real multi-year families):

- #108 — serve-time min-max decimation as a pure ChartData->ChartData transform in
  emit_chart (both single-run and family paths), bounding the served page to a few
  thousand points regardless of the underlying M1 point count; full data stays on
  disk; the WFO null-fill page collapses for free. Engine/registry untouched.
- #102 — a ChartMeta threaded from RunManifest into window.AURA_TRACES, rendered by
  a new pure buildHeader in chart-viewer.js (headless .mjs guarded), showing
  name/window/broker/commit/taps (+ member count for a family).

Signed under /boss on the Step-5 grounding-check PASS (10/10 load-bearing
assumptions ratified by green tests).

refs #108
refs #102
2026-06-22 11:36:22 +02:00
Brummel f51d54a1c1 docs(cell): name Scalar as the param-authoring surface; no tagged Cell ctors by design
Cell is the kind-erased hot-path carrier (C7): the value carries no tag, so
there are deliberately no tuple-style/tagged constructors (`Cell::I64(..)`).
Document on the struct that Scalar is the value to reach for when authoring a
param / bind / sweep point / RunManifest field, and that the from_* ctors are the
carrier-side entry. Resolves the discoverability papercut without softening the
carrier split.

closes #100
2026-06-22 11:24:08 +02:00
Brummel 5a01179fd5 docs(ledger): record the analysis meta-level as an open thread pointing to #109
Add an "Open architectural threads" entry that ties together aura's differentiator
(C21): the composable-orchestration rebuild (sweep/MC/walk-forward as composable
tools, not hard-wired CLI verbs) + the project-as-crate authoring layer (aura new /
experiment-builder API / Aura.toml / cdylib loading, C16/C17). Records the
load-bearing open fork — "driven via the CLI" = running a Rust-authored experiment
(compatible with C16/C17/C22) vs wiring nodes through the CLI itself (a deliberate
amendment to C17/C22/C9) — to be resolved in the brainstorm before any cycle.

Keeps the meta-level in focus in the always-loaded design memory; #109 is its
tracker home. No design decided here (the fork is recorded, not resolved).

refs #109
2026-06-22 11:10:51 +02:00
Brummel 53eeba5e16 audit(families-view): cycle close 0061 — drift-clean, ledger records the comparison view
Architect drift review over 99fd32b..HEAD (the housekeeping commits #99/#98 plus
the 0061 cycle): code drift-clean. Engine untouched (aura-engine + aura-core diff
empty — C9/C14); C21 realized faithfully (the comparison view consumes a set of
runs, a family is the first producer, no orchestration-axis rebuild); C18/C10
refuse-don't-guess honoured (every new error path exits 2, never panics; all six
tracing entry points guarded before persist, so name resolution is total). The only
finding was a ledger-realization gap, lifted in this commit.

Ledger (docs/design/INDEX.md): added a C22 amendment recording the
families-comparison view as realized (#107) — aura chart <family> overlays one tap
across a family's members on a shared y-scale, union-ts spine giving one mechanism /
three correct readings, the write-guard totalising name resolution; reconciled the
two stale "families-comparison ... remain open" lines (the #104 note and the
open-threads list) and named the deferred follow-ons (#102 run-context header, #47
multi-column tap selection, the orchestration-composability rebuild).

Regression: cargo test --workspace green (no failures), clippy --workspace
--all-targets -D warnings exit 0, cargo build --workspace clean — verified by the
orchestrator before close. No baseline to update (aura's regression gate is the
gated determinism suite, which is part of the workspace tests).

Retire the cycle's ephemeral spec + plan (0061). refs #107
2026-06-21 18:58:32 +02:00
Brummel 4c64feb9ed feat(chart): aura chart <family> overlays a family's members; --tap + write-guard
`aura chart <name>` now branches on the name's on-disk kind: a single run charts
its taps as before; a FAMILY overlays one tap (default equity) across all its
members in one self-contained uPlot page, consistent across sweep / MC /
walk-forward. Members share ONE y-scale (they measure one quantity — that is what
makes the overlay comparable, unlike the single-run overlay of different taps),
and align on the union-ts spine via the existing join_on_ts: sweep/MC members
share the window (true overlay), walk-forward members are disjoint OOS windows
(null-complementary -> the stitched curve). One mechanism, three correct readings.

- build_comparison_chart_data (one labelled Series per member, shared y_scale_id);
  filter_to_tap (single-run --tap restricts to one tap; no --tap = all taps,
  unchanged); parse_chart_args (`<name> [--tap <t>] [--panels]`, any order);
  emit_chart name-kind branch; USAGE updated.
- Write-guard: ensure_name_free is now called once per tracing command (run /
  run --macd / run --real -> Run; sweep / mc / walkforward -> Family) so a name
  cannot be reused across kinds — name resolution stays a total function.

Refuse-don't-guess throughout (exit 2, never panic). Engine untouched (C9/C14).
Tasks 2-3 of plan 0061; Task 1 (registry) landed in 9b2adcb.

Verified: cargo test --workspace green (no failures; new unit + integration tests
incl. family-overlay, single-run --tap filter, cross-kind collision RED-then-green),
cargo clippy --workspace --all-targets -D warnings clean, cargo build --workspace
clean. Benign clippy-forced deltas vs the literal plan (no behaviour change): a
local MemberRows type alias (type_complexity), let-chains in the six guards
(collapsible_if), and dropping the now-dead TraceStoreError import.

closes #107
2026-06-21 18:54:17 +02:00
Brummel 9b2adcbb1b feat(trace-store): name classification, family resolver, write-guard
aura-registry gains the read-side of total trace-name resolution and the
write-guard that makes it total:
- NameKind {Run, Family, NotFound} + TraceStore::name_kind — top-level index.json
  is a single run; else ≥1 member subdir with index.json is a family; else absent.
- read_family -> Vec<FamilyMember{key, traces}> — every member of a family, read
  via the existing read("<name>/<key>"), sorted by key (deterministic, C1); an
  absent/empty family reads as empty (treat-as-absent), a malformed member
  propagates Parse.
- ensure_name_free(name, WriteKind) + a TraceStoreError::NameTaken variant —
  refuses only cross-kind name reuse, so the one ambiguous on-disk state (a name
  used by both a run and a family) is unreachable; same-kind overwrite stays Ok.

Task 1 of plan 0061. Engine untouched (C9/C14). Tests: cargo test -p aura-registry
green (18 passed, incl. the 3 new), clippy -D warnings clean.

Note: the implement-loop quality gate exhausted its re-loop oscillating on the
method NAME (ensure_name_free "implies unused" vs a rename diverging from the
ratified plan/callers). Orchestrator overrule: the ratified name stands — it is
fixed by the spec/plan/#107 and the Task-3 call sites, and the doc comment already
states that Ok means "writable here", not "unused". The implementation is green,
clippy-clean, and spec-faithful.

refs #107
2026-06-21 18:07:12 +02:00
Brummel da5068e10a plan: 0061 families-comparison view
Three tasks: (1) aura-registry — NameKind/FamilyMember/WriteKind, name_kind,
read_family, ensure_name_free + a NameTaken error variant; (2) aura-cli — the
comparison ChartData builder (shared y-scale, union-ts spine), filter_to_tap,
parse_chart_args, the emit_chart name-kind branch, the --tap flag, USAGE; (3)
aura-cli — the write-guard calls in the six tracing fns + collision tests. Each
task RED-first with per-crate gates; workspace test + clippy at the close.

refs #107
2026-06-21 17:49:42 +02:00
Brummel 5f31eccc7c spec: 0061 families-comparison view (boss-signed)
A read-side cycle: `aura chart <family>` overlays one tap (default equity) of all
members of a sweep / MC / walk-forward family in one self-contained uPlot page,
consistent across kinds via the existing union-ts spine. The view consumes a
"set of runs to compare" (a family is its first producer) — the first step toward
the programmable analysis meta-level (C21), without rebuilding the orchestration
axes. aura-registry gains name_kind / read_family / ensure_name_free (a write-guard
making name resolution a total function); aura-cli gains the comparison ChartData
builder + emit_chart branch + --tap. aura-engine untouched (C9/C14).

Brainstorm-ratified, grounding-check PASS (11/11 current-behaviour assumptions
ratified by green tests). Signed under /boss on the grounding PASS.

refs #107
2026-06-21 17:39:20 +02:00
Brummel e1ad0979de refactor(ger40-examples): source pip from instrument_spec, drop the baked literal
The runnable GER40/FRA40 examples baked `const PIP_SIZE = 1.0` and a dead pip
column in the compare INSTRUMENTS table — a second source of pip truth parallel
to the vetted `aura_ingest::instrument_spec` channel the CLI `--real` path already
uses (#22). Route them all through one `pip_size_of(symbol)` helper: build_harness
sources GER40 internally, ger40_breakout_blueprint gains a `pip_size` first arg fed
from the lookup, and compare's run_cell sources each instrument's pip per cell.

Value-identical (GER40/FRA40 both resolve to 1.0) and behaviour-preserving: the
gated determinism / blueprint param-space / world tests stay green unchanged, and
the frozen `sim-optimal(pip_size=1)` broker-label pins are untouched.

closes #98
2026-06-21 16:41:37 +02:00
Brummel 094d63bc47 fix(family-stdout): unify family-wrapper key order with the store
The sweep / walk-forward / mc family-member stdout lines routed the embedded
RunReport through serde_json::json! -> a serde_json::Value, which re-alphabetizes
the manifest keys (broker-first) and, for mc, even reordered the top-level keys
(seed after report). That diverged from the commit-first declaration order that
RunReport::to_json and the stored families.jsonl already use.

Splice the pre-serialized report into a hand-built line via family_member_line /
mc_member_line so each family-member stdout line is byte-identical to its
families.jsonl record (commit-first manifest, seed between family_id and report).
RED-first: the two new key-order tests fail against the old json! helpers and pass
after the format! rewrite.

closes #99
2026-06-21 16:31:52 +02:00
Brummel 99fd32b1f9 feat(real-family): realistic strategy lengths over real data
The built-in family grid was calibrated for the synthetic demo streams (18/60
bars), where trend SMA 2-5 / MACD 2-4-3 are the only lengths that warm. Over real
M1 data those are noise — ~9000 exposure sign-flips per month, a strategy trading
the bid-ask wiggle, not a trend.

DataSource::strategy_lengths() makes the grid data-kind-dependent (like
wf_window_sizes): synthetic keeps {2,3}x{4,5} + MACD 2/4/3 (byte-unchanged); real
uses {50,100}x{200,400} trend SMAs + standard 12/26/9 MACD. Threaded through
sweep_family + sweep_over (the walk-forward in-sample sweep). Over real EURUSD the
monthly flip count drops from ~9000 to ~130-310 — a genuine trend cross.

This is a demo-strategy calibration patch (ledger C22 amendment notes it as such);
the real answer is project-authored strategies owning their own grid (C9), deferred
to the project-env work.

Verified: cargo test -p aura-cli (all green, synthetic byte-unchanged),
clippy -D warnings clean; real sweep probe shows ~130-310 flips/month.

refs #106
2026-06-21 16:06:28 +02:00
Brummel 607e5487e9 audit(real-family): cycle close 0060 — ledger records the real-data family axis
Architect drift review (b92aab7..HEAD): the cycle is contract-clean — C9 (engine
untouched, whole change CLI-side), C6/C1 (real bars streamed from the pre-recorded
archive, real sweep byte-deterministic), C10 (refuse-don't-guess shared by run
--real and the family path), C12 (MC-exclusion fenced: mc --real exits 2). One
medium ledger-gap, now closed; one low item ratified as carry-on.

- Ledger: docs/design/INDEX.md C22 gains a "real-data family source" amendment
  (#106) recording the opt-in real-data axis on sweep/walkforward families, the
  DataSource provider, the MC exclusion (Fork A), and the oos{ns} key widening.
  This was the only drift — a documentation gap, not code drift.
- Ratified carry-on [low]: the real walk-forward member key oos{from.0} is now an
  epoch-ns integer; portable and collision-free, covered in practice by the
  walkforward_real test (>=2 distinct oos<ns> dirs) and the 9-window E2E probe.
  No explicit ns-uniqueness assertion beyond starts_with("oos") — acceptable; a
  dedicated pin is not worth an iteration.
- Retire ephemeral spec/plan 0060 (git rm) per the cycle-artifact lifecycle; the
  durable record is this ledger amendment + the git history.

No regression scripts configured (architect is the gate). Full workspace test +
clippy -D warnings green at close.

refs #106
2026-06-21 15:33:02 +02:00
Brummel 8e5d14b2bb feat(real-family): --real parser + dispatch wiring; complete the feature
Plan 0060 Tasks 4-5 (#106): wire `aura sweep|walkforward --real` end to end.

- A shared RealWindowGrammar accumulator holds the `--real <SYMBOL> [--from <ms>]
  [--to <ms>]` grammar (flag-repeat strictness, empty-symbol + window-without-real
  rejection) for both parsers, mirroring parse_real_args.
- parse_sweep_args grows the DataChoice 4th element; parse_walkforward_args is new
  (no --strategy axis). The sweep + walkforward dispatch arms become rest-matched,
  build the DataSource via from_choice (build-or-refuse on un-vetted symbol /
  absent data), and run. USAGE documents the --real tails. mc stays exact-matched,
  so `aura mc --real` falls through to usage + exit(2) — Fork A: MC excluded (its
  seed varies a synthetic price-walk, undefined over real data).
- Gated integration tests (skip on no local data): sweep --real EURUSD persists 4
  portable member dirs over real bars and charts; walkforward --real EURUSD
  persists one oos<ns> dir per rolling window; same real sweep twice is
  byte-identical (C1). Un-gated: un-vetted symbol, window-without-real, and
  mc --real each refuse with exit 2.
- Remove the now-redundant `#[allow(dead_code)]` from the Task-1 provider: every
  const, both enums, and every method are live once the wiring lands.

Verified end-to-end over real EURUSD data: sweep --real -> 4 members; walkforward
--real over 2024 -> 9 rolling OOS windows; all refusals exit 2. Full workspace
test + clippy -D warnings green.

closes #106
2026-06-21 15:29:28 +02:00
Brummel 2a6ca47acd feat(real-family): thread DataSource + pip through the family builders
Plan 0060 Tasks 2-3 (#106): the family builders now take a &DataSource and draw
their source, pip, window, and walk-forward roller sizes from it instead of the
hardcoded synthetic VecSource / SYNTHETIC_PIP_SIZE.

- sample_blueprint_with_sinks / momentum_blueprint_with_sinks take a pip_size
  (un-hardcode the SimBroker); every caller threads it.
- sweep_family / momentum_sweep_family / walkforward_family / sweep_over /
  run_oos take &DataSource; run_sweep / run_walkforward take a DataSource and the
  dispatch passes DataSource::Synthetic (the --real parser is the next step).
  Synthetic behaviour is byte-unchanged.
- Fix a design gap the implementer correctly bounced on: walk-forward is a
  windowed consumer whose span comes from the 60-bar walkforward_prices, NOT the
  18-bar showcase that full_window() returns. Added DataSource::wf_full_span()
  (synthetic walk-forward span; real = the probed --from..--to) and pointed
  walkforward_family at it. Without it the synthetic roller (24,12,12) over span
  (1,18) exits SpanTooShort.

Verified: cargo test -p aura-cli (all green, synthetic byte-unchanged),
cargo clippy -p aura-cli --all-targets -D warnings (clean).

refs #106
2026-06-21 14:55:39 +02:00
Brummel cf102172ec feat(real-family): DataSource provider + shared real-data helpers
Foundation for `aura sweep|walkforward --real` (#106, plan 0060 Task 1).

- New CLI-side `DataChoice` (parsed) / `DataSource` (opened) provider: the
  synthetic-vs-real source choice as one type, supplying a member's source, pip,
  full/windowed sources, and walk-forward roller sizes. Not yet wired into the
  family builders (plan Tasks 2-5); `#[allow(dead_code)]` marks the wired-later
  items, to be removed once the builders thread `&DataSource`.
- DRY: `run_sample_real`'s inline pip-refusal / no-data refusal / probe-window
  logic is extracted to shared `instrument_spec_or_refuse` / `no_real_data` /
  `probe_window` helpers, now reused by `DataSource::from_choice` / `full_window`
  instead of duplicated.
- Real walk-forward roller-size constants (90d / 30d / 30d in ns, Fork D/F).

The synthetic provider is consumer-dependent by design (full-window consumers draw
showcase_prices; windowed consumers draw walkforward_prices) so the byte-unchanged
guarantee holds across both family paths.

Verified: cargo test -p aura-cli (all green incl. byte-unchanged synthetic path),
cargo clippy -p aura-cli --all-targets -D warnings (clean). Full-workspace gate at
iter close after Tasks 2-5.

refs #106
2026-06-21 14:40:15 +02:00
Brummel bd5a08d71f plan: 0060 real-data source path for family runs
Five compile-gated tasks for spec 0060: (1) additive DataSource/DataChoice
provider + ns roller constants, (2) pip-parametrize the two blueprints + thread
all callers, (3) thread &DataSource into the five family builders + run_sweep/
run_walkforward (dispatch passes Synthetic), (4) --real parser grammar +
build-or-refuse dispatch + USAGE, (5) gated real-path integration tests + full
suite + clippy. Each task threads its signature change with all callers so
`cargo build -p aura-cli --all-targets` is green per task; the byte-unchanged
synthetic path is the regression guard.

refs #106
2026-06-21 14:17:49 +02:00
Brummel 9637730520 spec: 0060 real-data source path for family runs (boss-signed)
Opt-in `--real <SYMBOL> [--from <ms>] [--to <ms>]` for `aura sweep` and
`aura walkforward`, mirroring `aura run --real`: each family member streams real
M1 close bars from the local data-server archive instead of the synthetic stream.
MC is excluded — its seed varies a synthetic price-walk realization, undefined
over real data (one realization -> identical members; a real MC needs a
bootstrap-resampling axis, its own cycle).

Engine, ingest, and registry untouched: a CLI-side `DataSource` provider replaces
the hardcoded `VecSource`. `M1FieldSource` already impls `Source`; `WindowRoller`
already rolls over epoch-unit timestamps.

Boss-signed: Step-5 grounding-check PASS — all 8 load-bearing current-behaviour
assumptions ratified by named, currently-green tests.

refs #106
2026-06-21 14:05:34 +02:00
Brummel b92aab713c audit(sweep-key): cycle close 0059 — drift-clean, retire spec/plan
Cycle-close tidy for the generic portable sweep member-key + bool-param demo
strategy (spec/plan 0059, reference issue #105, implementations 27f850d + 0b73a75).

Architect drift review (range 1d6fafb..HEAD): clean — no drift, no debt.
- C1 (determinism): varying_axes + member_key are pure functions of the grid
  point, no schedule state; the engine carries no trace state (consistent with
  the #104 design + the C22/#101 ledger note).
- C7/C8/C9: LongOnly is a clean one-in/one-out f64 node, None until warmed (C8
  filter), the first bool *param* through the already-ratified Cell::bool()
  path; momentum is an ordinary composite of existing nodes; brokers stay
  downstream (C10). C18/C21/C22 upheld.
- The engine accessor exposes the binder's OWN axis-cardinality knowledge (it
  holds the value-lists), not a presentation concern — formatting stays in the
  CLI. The hand-rolled fnv1a64 mirrors the ledger-blessed SplitMix64 precedent
  (harness.rs): tiny, dependency-free, bit-stable-on-purpose (std DefaultHasher
  is unstable across releases; on-disk-name stability is the per-case C16
  reason). LongOnly is a genuine universal aura-std block (long-only is
  asset-agnostic). The ledger amendment matches the code.

Regression: the project declares no dedicated regression command; the gate is
the architect review plus the full suite, verified green at 0b73a75 (cargo test
--workspace, clippy --all-targets -D warnings).

Spec docs/specs/0059 and plan docs/plans/0059 retired (git rm) per the ephemeral
active-cycle artefact lifecycle; the durable rationale (the portable member-key
convention) lives in the ledger amendment (docs/design/INDEX.md C22/#101 note)
landed in 0b73a75.

closes #105
2026-06-21 11:35:59 +02:00
Brummel 0b73a75d4b feat(momentum): bool-param demo strategy + aura sweep --strategy selector
Proves the #105 generic portable member key end-to-end: a demo strategy whose
params have nothing in common with the SMA-cross demo, swept by the same
machinery, with a bool axis surfacing — conformant — in the member dir names.

- momentum strategy: price -> Ema(length:i64) -> Sub(price-ema) ->
  Exposure(scale:f64) -> LongOnly(enabled:bool) -> SimBroker. Three swept params
  of three kinds incl. a bool; the exposure sink taps the post-gate exposure so
  the bool's effect is in the trace.
- aura sweep gains --strategy <sma|momentum> via a pure, unit-tested
  parse_sweep_args (mirroring parse_real_args); default = SMA-cross, so the
  existing bare / --name / --trace forms are unchanged.
- `aura sweep --strategy momentum --trace mom` persists 8 member dirs named e.g.
  ema.length-5_exposure.scale-0.5_longonly.enabled-true — the bool in the dir
  name, every name matching [A-Za-z0-9._-], each chartable.
- ledger (docs/design/INDEX.md): the C22/#101 member-key note's sweep clause
  amended to the generalised portable form (MC seed{N} / walk-forward oos{ns}
  unchanged).

Self-verified: cargo build --workspace, cargo test --workspace (all green incl.
momentum_param_space_is_ema_exposure_longonly, the 8-point determinism test,
the parse_sweep_args grammar test, and the momentum_sweep_trace integration test
asserting 8 portable bool-bearing dirs + a chartable member), cargo clippy
--workspace --all-targets -D warnings.

refs #105
2026-06-21 11:32:46 +02:00
Brummel 27f850dc52 feat(sweep-key): generic filesystem-portable member key + LongOnly bool node
Generalises the #105 foot-gun: the family-member trace key was derived from two
hardcoded axis names (f{fast}s{slow}), collision-free only over the built-in SMA
grid. It is now derived from the axes that actually VARY
(SweepBinder::varying_axes) and rendered as a filesystem-conformant directory
component:
- charset restricted to [A-Za-z0-9._-] (valid on Linux/Windows/macOS, also
  URL-path- and cloud-sync-safe); any other byte sanitised;
- case-less values (i64/timestamp decimal, bool true/false, f64 via Display, no
  scientific notation) so two members of one family never differ only by letter
  case -> no silent overwrite on case-insensitive filesystems (NTFS/APFS);
- length-capped (MAX_KEY=200) with a version-stable FNV-1a fallback for the
  unbounded-grid edge.

Pinned singleton axes are omitted; the SMA sweep's member dirs change from f2s4
to signals.trend.fast.length-2_signals.trend.slow.length-4 (its --trace
integration test + the stale prose updated accordingly).

Adds the first aura-std node with a bool *param*, LongOnly (a long-only exposure
gate: enabled=true clamps short exposure to >=0, false passes through) — the
block the momentum demo strategy (next commit) sweeps to prove the key generic
over a bool axis. The engine change is one additive pure read accessor; no
existing signature changes, trace state stays out of the engine.

Self-verified: cargo build --workspace, cargo test --workspace (all green incl.
the LongOnly node tests, the member_key worked-examples corpus, the varying_axes
accessor test, and the updated SMA sweep-trace integration test), cargo clippy
--workspace --all-targets -D warnings.

refs #105
2026-06-21 11:15:22 +02:00
Brummel 44e3a1a678 plan: 0059 generic portable sweep key + bool-param demo strategy
Decomposes spec 0059 into 5 tasks: (1) engine SweepBinder::varying_axes
accessor; (2) aura-std LongOnly bool-param node; (3) the portable member_key
renderer replacing sweep_member_key (+ sweep_family refactor + the SMA
sweep-trace assertion update); (4) the momentum strategy + --strategy selector
(+ the momentum integration test); (5) ledger amendment + full-suite/lint gate.
Each task is RED-first and builds green per task.

refs #105
2026-06-21 10:55:20 +02:00
Brummel a8ba019d4d spec: 0059 generic portable sweep key + bool-param demo strategy (boss-signed)
Generalises the family-member trace key (#105 foot-gun): derive it from the
axes that actually vary (not two hardcoded axis names), and render it as a
filesystem-conformant directory name on every major OS — charset [A-Za-z0-9._-],
case-less values (no case-insensitive-FS collision), length-bounded with an
FNV-1a fallback for the unbounded-grid edge.

Adds the first bool *param* node (LongOnly) and a momentum demo strategy
(Ema -> Sub -> Exposure -> LongOnly -> SimBroker) swept via a new
`aura sweep --strategy <sma|momentum>` selector — the empirical proof the
generic key works for a strategy whose params (incl. a bool) differ entirely
from the SMA-cross demo.

Boss-signed on the Step-5 grounding-check PASS (an independent fresh-context
agent): all 9 load-bearing current-behaviour assumptions ratified by green
tests; the end-to-end bool-param sweep path is correctly scoped as new
behaviour proven by new tests, not assumed. Workspace type-checks clean.

refs #105
2026-06-21 10:41:31 +02:00
Brummel 1d6fafbc39 audit(family-traces): cycle close 0058 — drift-clean, retire spec/plan
Cycle-close tidy for family-member trace persistence (spec/plan 0058,
reference issue #104, implementation d3cb5f8).

Architect drift review (range 06fdafa..d3cb5f8): drift_found, low-grade
only — no contract violated. C1 (determinism), C7 (SoA), C8 (sinks),
C18/C21 (registry/families) all upheld; the opt-in --trace path is
genuinely byte-neutral. Two items, both resolved here:

- [high] Ledger gap: the C22/#101 disk-trace note recorded only the
  single-run runs/traces/<name>/ layout. This cycle establishes a new,
  load-bearing on-disk convention (per-member nested standalone run-dirs
  runs/traces/<name>/<member_key>/) that the deferred family-comparison
  view will build on. FIXED: amended the C22/#101 realization note in
  docs/design/INDEX.md to record the layout, the content-derived
  deterministic member keys, and the parallel-safe lock-free write.

- [medium] sweep_member_key hard-codes two grid axes; collision-free only
  over today's built-in grid, a latent foot-gun if the grid varies an
  unlisted axis. Not a current defect (one grid, covered, fail-loud on a
  missing listed axis). CARRY-ON: filed #105 (idea) for generalised key
  derivation when the grid generalises.

Regression: the project declares no dedicated regression command; the
gate is the architect review plus the full suite, verified green at
d3cb5f8 (cargo test --workspace, clippy --all-targets -D warnings).

Spec docs/specs/0058 and plan docs/plans/0058 retired (git rm) per the
ephemeral active-cycle artefact lifecycle; durable rationale lifted to
the ledger amendment above.

refs #104
2026-06-20 20:01:13 +02:00
Brummel d3cb5f8052 feat(family-traces): persist per-member streams for sweep/MC/walk-forward
Family runs (sweep, Monte-Carlo, walk-forward) ran a full harness per
member and drained its equity+exposure recorders, but immediately folded
the streams to metrics and discarded them — so the C21 family-comparison
view had no per-member data to chart. A new opt-in `--trace <name>` on
`aura sweep|mc|walkforward` now persists each member's streams to disk,
mirroring the single-run `aura run --trace` path. Any single member is
chartable today via `aura chart <name>/<member_key>`; the comparison view
itself is a later cycle.

Design:
- Persist INSIDE each per-member closure (aura-cli), reusing persist_traces
  verbatim — the engine and the family types are untouched. The closures
  already held the drained rows; they now bind them once, build the
  manifest, persist when --trace is set, then fold as before.
- Content-derived, deterministic member keys (MC seed{N}, sweep f{fast}s{slow},
  walk-forward oos{ns}). The engine HOFs are Fn + Sync — members run in
  parallel, so a runtime ordinal counter would be non-deterministic (a C1
  break); keys derive from what each closure receives deterministically.
  sweep_member_key panics (naming the axis) if the grid lacks a key axis,
  rather than silently collapsing two points to a shared dir.
- Each member is a standalone run-dir at runs/traces/<name>/<member_key>/
  (persist_traces with a slash-name; TraceStore nests via create_dir_all),
  so the existing single-run viewer charts a member with no view-side code.
- Opt-in: without --trace, stdout and the run registry are byte-unchanged;
  the cardinality cost of N members x full resolution stays user-chosen.

Out of scope (deferred): the family-comparison view (overlay/small-multiples),
decimation/LOD, a binary trace container.

Verified locally: cargo build --workspace, cargo test --workspace (all green,
incl. 3 new family persistence tests + a sweep determinism test + 2
sweep_member_key unit tests), cargo clippy --workspace --all-targets
-D warnings (clean). The pre-existing family determinism and single-run
trace tests pass unchanged (no-trace path is byte-identical).

closes #104
2026-06-20 19:56:36 +02:00
Brummel f33b427883 plan: 0058 family-trace-persistence
Three tasks (MC, sweep, walk-forward) wiring per-member trace persistence
into the family closures + a full-suite/lint gate. Each task threads its
builder signature and every caller together so the workspace compiles
green per task.

refs #104
2026-06-20 18:51:32 +02:00
Brummel 04f13af88a spec: 0058 family-trace-persistence (boss-signed)
Persist each family member's equity/exposure streams to disk (opt-in
--trace on sweep|mc|walkforward), mirroring the single-run trace path, so
the C21 family-comparison view has per-member data and any single member
is chartable today. Write side only; the comparison view, decimation,
and a binary trace container are deferred.

Autonomously signed under /boss on the Step-5 grounding-check PASS.
refs #104
2026-06-20 18:40:41 +02:00
Brummel 06fdafa925 feat(trace-charts): keyboard chart control — pan / zoom-toward-cursor / reset / toggle
Keyboard control for the served chart, deliberately the discrete counterpart to the
glitchy hand-rolled pointer drag that was just removed: each key is one clean
setScale("x", …), deterministic, with none of uPlot's pointer-pan jank.

- arrows pan (15% of the visible span), +/- zoom, 0/Home reset, x toggles the spine.
- zoom anchors on the CURSOR: the x-value under the mouse keeps its relative position,
  so the point under the pointer stays put while zooming (centre when the mouse is off
  the chart). The mouse position is tracked per chart via posToVal on mousemove.
- keys drive every chart instance together, so panels stay x-aligned.
- uPlot's default mouse (drag = box-zoom, double-click = reset) is retained — you have
  both.

The load-bearing math is the pure keyXRange(key, lo, hi, anchor, ext) helper (the rest
is browser-only DOM wiring in mount); it is node-guarded by chart_viewer_keys.mjs, which
pins the zoom-toward-cursor invariant (anchor keeps its relative position) with an
off-centre anchor so a centre-zoom regression fails loudly. All five chart guards green.

refs #103
2026-06-19 23:28:54 +02:00
Brummel 08a272c73d fix(trace-charts): drop glitchy custom wheel/pan, restore uPlot default mouse control
The hand-rolled wheel-zoom + drag-pan (wheelZoomPlugin) was hakelig/glitchy —
uPlot is not built to be driven that way. Remove the plugin and the cursor.drag
override so uPlot's own defaults govern the mouse: drag = box-zoom on x,
double-click = reset to full range, with the legend value-readout and crosshair
unchanged.

Everything else from the Tier-1 pass stays: the ordinal (gap-collapsed) x-axis
default + continuous toggle, the ordinal-label honesty (ticks/legend report the
real xs[i]), and panel cursor-sync (cursor.sync key retained; only the drag
override and the plugin are dropped). The x-mode guard is flipped to pin the
revert — overlay carries no custom plugin and no cursor.drag override, panels
carry no plugin — and stays green alongside the other three chart guards.

refs #103
2026-06-19 23:15:39 +02:00
Brummel 87e6e8992d audit(viewer-tier1): cycle close — drift-clean, retire spec/plan 0057
Architect drift review over 5bff7e0..HEAD: clean, no drift, no debt. C14/C7
firewall intact (main.rs byte-unchanged → build_chart_data still returns
ChartData{xs,series}, the injected window.AURA_TRACES payload shape untouched;
the ordinal index spine is derived client-side). C22 holds — the
ordinal/continuous toggle + wheel-zoom/drag-pan/dblclick-reset are view
affordances over a recorded trace (read-only setScale on the x range), not a
scene editor. The #102 manifest-header gap is neither closed nor regressed,
still tracked.

Regression gate: cargo test --workspace green (incl. the four chart guards +
render inline tests), clippy --all-targets -D warnings clean; aura has no
separate regression script, so the suite + architect are the gates. cycle
clean.

Spec/plan 0057 retired per the ephemeral-artifact lifecycle (durable record:
the feature commit 45cc1c4, the #103 decision-log comments, and git history).

refs #103
2026-06-19 19:03:16 +02:00
Brummel 45cc1c44c9 feat(trace-charts): ordinal x-axis default + zoom/pan/cursor-sync (viewer Tier-1)
The served trace chart's x-axis no longer plots against raw timestamps by
default — non-trading gaps (nights, weekends) left horizontal blanks that
dominated the view. The x spine is now ordinal by default: every recorded slot
sits at an even position, so gaps collapse and the signal fills the width. A
header toggle flips to continuous (real-time-proportional) spacing and back;
default ordinal honours the requested "x-axis should not be continuous", with
continuous as the one-click option.

Comfort, all on the read/render side (the engine and the Rust serve contract
are untouched — window.AURA_TRACES still carries the real xs, the ordinal
indices are derived client-side):
- ordinal mode keeps the axis honest: ticks and the legend value-readout map a
  collapsed index back to the real xs[i], never the index itself.
- wheel-zoom (x, cursor-anchored), drag-pan, double-click-reset on the plot
  (uPlot's default drag-box-zoom is disabled so drag pans).
- panels share a cursor.sync key, so the crosshair lines up across stacked
  panels.

Confined to chart-viewer.js (buildCharts gains an xMode param + a wheelZoomPlugin
factory; mount re-renders on the toggle) and render.rs (CHART_HEAD toggle button
+ a chart-only style). The two twin node guards (build/gaps) re-point to the
ordinal default in lockstep; a new x-mode guard pins ordinal-default / continuous
/ panel-sync / plugin wiring, and a label-honesty guard (gappy-traces fixture,
xs far from the index spine) pins that ordinal ticks report the real timestamp.

Verified: cargo test --workspace green (incl. the four chart guards + render
inline tests), clippy --all-targets -D warnings clean, e2e (aura run --trace +
aura chart) emits a self-contained page defaulting to ordinal with the toggle.

Spec docs/specs/0057, plan docs/plans/0057 (boss-signed; grounding-check PASS).
Derived-fork decision log in the issue comments.

closes #103
2026-06-19 19:00:33 +02:00
Brummel bb8045ea59 plan: trace-chart viewer Tier-1 — ordinal x-axis + zoom/pan/cursor-sync
Five tasks: RED x-mode guard; buildCharts gains xMode (ordinal default) +
wheelZoomPlugin + panel cursor-sync, with the two twin guards re-pointed in
lockstep; mount render(xMode)+toggle; render.rs CHART_HEAD toggle + chart-only
style + chrome tests; full-suite/clippy/e2e gate.

refs #103
2026-06-19 18:23:42 +02:00
Brummel be71f0a55c spec: trace-chart viewer Tier-1 — ordinal x-axis + zoom/pan/cursor-sync (boss-signed)
Tier-1 comfort pass for the served trace chart (#103): an ordinal x-axis
default (collapses non-trading gaps) with a one-click continuous toggle, plus
wheel-zoom / drag-pan / dblclick-reset, a value-readout legend, and panel
cursor-sync. Confined to the read/render side (chart-viewer.js + render.rs
CHART_HEAD); the Rust serve contract is unchanged.

Boss-signed: grounding-check PASS (five current-behaviour assumptions ratified
by green node guards + render.rs inline tests). Derived-fork decision log in
the #103 comments.

refs #103
2026-06-19 18:16:03 +02:00
Brummel 5bff7e0fb9 audit(trace-charts): cycle close — C22 realization note, retire ephemera
Cycle-close audit for the trace-charts visual face (#101, shipped across
8bb5256 persist + 15ee6d5 serve). Architect drift review (13941a2..HEAD)
+ the regression gate (cargo build/test/clippy --workspace, all exit 0,
orchestrator-verified).

What holds (carry-on): C14 clean — aura-engine/aura-core carry zero
UI/HTML/uPlot knowledge; ColumnarTrace does struct->encoding only, file
I/O in aura-registry, rendering in aura-cli. C1/C3 clean — persistence
and serve-time alignment are post-run pure reductions (join_on_ts at
serve time only, never in-graph). C7 clean — on-disk f64 columns +
kinds-tagged base types, the documented coercion honest, no 5th scalar.

Resolutions:
- [fix] Added the C22 realization note now that the web-from-disk seam
  shipped (the amendment 3b56efb had deferred it to "when the seam
  ships").
- [ratify] The served page injects the chart SERIES but drops the run
  manifest (no run-context header) — a deliberate, defensible naive-viewer
  scope call (the manifest is on disk in index.json; a header matters most
  for the deferred families/comparison work, C21). The deviation from spec
  0056 §1/§4 landed unflagged in 15ee6d5; it is now flagged here and
  tracked as a follow-up. refs #102
- [tidy] Retired this cycle's ephemera (spec 0056, plans 0055/0056) and
  the stale debris from the prior Runway close (spec 0055-fieldtest) per
  the ephemeral-artifact convention; the durable record is the ledger +
  git history.

Cycle drift-clean (modulo the ratified, tracked manifest-header gap).

refs #101
2026-06-19 00:59:54 +02:00
Brummel 15ee6d5f4f feat(trace-charts): serve persisted traces as a web chart (iter 2)
Iteration 2 (serve half) of the visual face — spec 0056 / plan 0056:
read a persisted run's traces and serve them as a self-contained uPlot
HTML chart. Completes the goal "tap data from a graph, serve it as a
chart in the browser".

- chart-viewer.js (first-party): a pure buildCharts(traces, mode) that
  maps the injected window.AURA_TRACES to uPlot configs (no DOM) — overlay
  = one plot, every series on its own y-scale over the shared xs; panels =
  one plot per series, all on the same timestamp x. A browser-only mount()
  instantiates the vendored global uPlot. Guarded by a node .mjs DOM test
  (+ a sparse/gaps variant) shelling out exactly like viewer_dot.rs.
- render_chart_html + ChartMode/ChartData/Series (aura-cli/render.rs):
  mirrors render_html — self-contained page, uPlot (1.6.32, vendored
  d534f10) + chart-viewer.js inlined via include_str!, data injected as
  window.AURA_TRACES.
- emit_chart + build_chart_data (aura-cli/main.rs): the spec-§6 3-step
  union-spine alignment — xs = sorted-dedup union of every tap's ts; a
  synthetic empty-payload spine + ALL taps as symmetric join_on_ts sides
  (no tap privileged, no point dropped); flatten each (tap,column) to a
  Series of Option<f64>. New arms `aura chart <name> [--panels]`; a
  missing run is stderr + exit 2 (TraceStoreError::NotFound), never a
  panic. USAGE advertises the verb in lockstep.

Verified by the orchestrator: `cargo test --workspace` green (incl. the
node guards, render_chart_html, and the chart e2e); `cargo clippy
--workspace --all-targets -D warnings` exit 0; and a real end-to-end —
`aura run --trace demo && aura chart demo` emits a 57KB self-contained
page (doctype + inlined uPlot + AURA_TRACES carrying the equity/exposure
series), `--panels` reflects the panels mode, `aura chart nope` exits 2.
Implementer deviation folded in (compiler-prescribed): serde added to
aura-cli/Cargo.toml for the Series derive (Cargo.lock updated).

refs #101
2026-06-19 00:52:45 +02:00
Brummel 709a68a2ae plan: trace charts iter 2 (serve) — render_chart_html, chart-viewer.js, aura chart verb
Iteration-2 (serve half) plan for spec 0056: a first-party chart-viewer.js (pure buildCharts + browser mount) with a node .mjs DOM guard; render_chart_html + ChartMode/ChartData/Series mirroring render_html; emit_chart + the spec-§6 union-spine join_on_ts alignment + the `aura chart <name> [--panels]` arms. uPlot already vendored (d534f10). RED-first; no existing-signature changes (no caller blast radius).

refs #101
2026-06-19 00:25:06 +02:00
Brummel d534f106da chore(aura-cli): vendor uPlot 1.6.32 for the trace-chart viewer
Vendors uPlot.iife.min.js (the IIFE bundle exposing the global `uPlot`) + uPlot.min.css under assets/, pinned at uplot@1.6.32 via unpkg, mirroring the existing Graphviz-WASM/pan-zoom vendoring (checked in verbatim so include_str! + the build stay hermetic/offline, C1/C8). refresh-assets.sh gains the matching pinned-fetch lines. Consumed by iteration 2's render_chart_html (serve half of spec 0056).

refs #101
2026-06-19 00:20:30 +02:00
Brummel 8bb5256041 feat(trace-charts): persist recorder taps to disk (iter 1)
Iteration 1 (persist half) of the visual face — spec 0056 / plan 0055:
tap data from a graph and save it to disk so it can later be charted
(the serve/chart half is iteration 2).

- ColumnarTrace (aura-engine): a drained tap <-> columnar SoA form
  ({tap, kinds, ts, columns}); values coerced to f64 for plotting with
  the base type preserved in `kinds` (C7); to_rows rebuilds uniformly-f64
  rows for the serve-side join. Pure (C1), beside join_on_ts/summarize.
- TraceStore (aura-registry): a per-run directory under
  runs/traces/<name>/ — one <tap>.json per tap + an index.json (manifest
  + tap order), beside the families.jsonl sibling store. Missing run =
  NotFound (no panic); corrupt file = Parse{file}.
- CLI: a shared persist_traces helper threaded into run_sample /
  run_macd / run_sample_real via an orthogonal `trace: Option<&str>`;
  new arms `aura run [--macd] --trace <name>` and `--trace <name>` on
  `run --real`. The default (no --trace) run is byte-for-byte unchanged.

Verified by the orchestrator: `cargo test --workspace` green (incl. the
4 columnar_trace, 3 trace_store, 4 cli_run e2e, and the 4-tuple
parse_real_args tests); `cargo clippy --workspace --all-targets -D
warnings` exit 0. Implementer deviations folded in, all
compiler-prescribed: #[derive(Debug)] on RunTraces (the RED tests
panic-format a Result over it), #[allow(clippy::type_complexity)] on
the parse_real_args 4-tuple (mirrors the crate's sample_harness idiom).
Default-run byte-identity stays pinned by run_prints_json_and_exits_zero.

refs #101
2026-06-19 00:14:18 +02:00
Brummel 99a18ff00c plan: trace charts iter 1 (persist) — ColumnarTrace, TraceStore, --trace wiring
Iteration-1 (persist half) plan for spec 0056: ColumnarTrace (aura-engine), TraceStore (aura-registry), and the shared persist_traces helper threaded into all three run forms. RED-first; Task 3 folds all nine signature-change callers in before the build gate (per plan-recon's compile-gate warning). Serve/chart half deferred to iteration 2.

refs #101
2026-06-18 23:41:42 +02:00
Brummel bcf48f3f86 spec: trace charts — persist taps + serve web charts (auto-signed)
First cut of aura's visual face (issue #101; the web-from-disk pivot
ratified into C14/C22 this session, commit 3b56efb): tap arbitrary
recorder streams from a run, persist them to disk as columnar (SoA)
JSON, and serve them as a self-contained uPlot HTML chart — feeds
overlaid on a shared timestamp axis, or timestamp-aligned in stacked
panels. `aura run [--macd] [--trace <name>]` and `aura run --real <SYM>
... [--trace <name>]` persist; `aura chart <name> [--panels]` serves.
Both modes share one synthetic-union-spine join_on_ts alignment.

Signed via the specify auto-sign gate: the user is away under an
explicit "work fully autonomously, document decisions" mandate, so no
manual signature was available. All objective gates green (precondition
clean, self-review clean, grounding-check PASS), and a unanimous 5-lens
adversarial spec-skeptic panel (criterion / grounding / scope-fork /
ambiguity / plan-readiness all SOUND).

The panel earned its keep — two self-correction rounds to unanimity:
- Round 0 caught (a) panels shipped NOT timestamp-aligned, contradicting
  the user's verbatim "aligned nach Timestamps in Panels" — corrected to
  conform to issue #101 Fork A; (b) the USAGE advertised `--macd --trace`
  with no dispatch arm — corrected to an orthogonal `--trace` composing
  with every run form via a shared persist helper.
- Round 1 caught the union-spine -> join_on_ts mapping under-specified
  (which tap is the spine?) — corrected with an explicit 3-step
  synthetic-empty-spine mechanism (no privileged tap, no points dropped),
  plus a to_rows uniformly-f64 clarification.

The two delegated naive-viewer decisions (per-series y-scale, union
spine) are documented with provenance on issue #101.

refs #101
2026-06-18 23:28:58 +02:00
Brummel 3b56efbdb4 docs(design): amend C22/C14 visual face — web-from-disk, not egui
The visual face pivots from the ledger's egui-native, in-process
zero-copy direction to a web frontend served from disk-persisted
recorder traces: the engine writes recorded traces to disk, a browser
charts them. Settled in an in-context design discussion (provenance in
issue #101): raw per-tap trace persistence + serve-time join_on_ts
alignment + a static self-contained HTML chart (uPlot). The engine
stays headless (C14) — arguably more strictly, since a browser reading
a serialized file is further from the hot path than egui reaching into
the live SoA columns. Amends the C14 visual-face note and the open
"Playground & World UI surface" thread; the C22 realization note lands
when the seam ships.

refs #101
2026-06-18 21:45:03 +02:00
Brummel 13941a24d5 fieldtest: milestone runway — real-data seam, 4 examples, 6 findings (gate green)
Milestone-close fieldtest for "Runway — real-data ergonomics & honesty
hardening". Four curated end-to-end scenarios derived top-down from the
milestone promise, run as a downstream consumer against real GER40/EURUSD M1
bars (public interface only; no crates/*/src read):

  1. per-instrument pip honesty (#22) — GER40 index pip 1.0 AND EURUSD forex
     pip 0.0001 both honest end-to-end; AAPL.US/FOOBAR un-vetted refused
     exit 2, no stdout leak
  2. real GER40 M1 open via open_ohlc from aura-ingest ALONE (#80/#81/#92) —
     no data-server dep, 53173 real bars, C4 O/H/L/C order, ts-window inverse
  3. multi-tap ts-join (#93) — two taps at different cadences, 27744 rows,
     0 by-ts mismatches; warm-up None rows where a positional zip would corrupt
  4. run-registry coherence (#73/#82) — list/rank retired exit 2, families/
     family live, two-store Registry read-back + rustdoc

Findings: 4 working, 1 friction, 1 spec_gap.
  - friction: Cell exposes only from_i64/from_f64/... while the side-by-side
    Scalar is a tagged enum with public I64(..)/F64(..) variants. The natural
    param-authoring entry point is Scalar (Cell is the hot-path buffer carrier),
    so the wrong reach is a self-correcting compile snag — ergonomic only.
    Filed as a follow-up idea.
  - spec_gap: unknown family id exits 0 empty — ratified into the C18 note in
    the preceding commit (treat-as-empty contract).

Gate verdict: GREEN. All seven axes mapped to a covering scenario and exercised
on real bars; both finding classifications and the one material coverage gap
were adversarially re-verified. That gap — #22's FX-vetted pip face, untested by
the original scenario set — was probed (EURUSD -> sim-optimal(pip_size=0.0001),
FX-scaled, not index-mis-scaled) and folded into scenario 1, so the pip promise
is demonstrated on both positive faces. Every scenario re-run independently on
HEAD before commit.
2026-06-18 21:13:56 +02:00
Brummel 5a14d9cc44 docs(design): ratify C18 unknown-family-id contract (treat-as-empty, exit 0)
The Runway milestone fieldtest surfaced a spec_gap: `aura runs family <id>`
with an unknown-but-well-formed id exits 0 with empty output, and neither the
ledger nor the glossary constrained that case. The behaviour is already the
documented, tested CLI contract (treat-as-empty, mirroring Registry::load's
missing-store = Ok(empty)); this lifts that contract to the durable C18 note
so a consumer reasoning from the spec can derive it, and records that tightening
to a non-zero "no such family" exit (typo-safety) stays an available future UX
choice rather than a current contract.

Ratifies the spec_gap from docs/specs/0055-fieldtest-runway-real-data-seam.md.
2026-06-18 21:13:17 +02:00