fieldtest: milestone walking-skeleton — 3 e2e scenarios, delivers end-to-end

Milestone-scope fieldtest (the functional leg of the Walking-skeleton
milestone-close gate), from the public interface only. Three end-to-end
scenarios, all green:
- the newcomer CLI path: `aura run` -> single-line {manifest,metrics} JSON,
  exit 0, byte-identical twice (C1), non-degenerate trace (C22), bad-args ->
  exit 2;
- the real-data thread: real AAPL.US 2006-08 M1 bars -> load_m1_window ->
  close_stream -> SMA-cross -> Exposure -> SimBroker -> summarize -> RunReport,
  deterministic AND populated;
- epoch-ns coherence: C3 normalization holds ingest -> run -> sink.

Roll-up: the milestone DELIVERS its promise end to end (ingest -> one signal ->
exposure -> sim-optimal broker -> pip-equity metric -> structured RunReport,
deterministic, populated) on BOTH the synthetic and real-data paths. 0 bugs.

Headline concern (verified by the orchestrator): issue #19's premise is WRONG at
the shipped SMA(2)/SMA(4) demo config — 21 AAPL.US bars warm the cross and
produce a populated trace (total_pips=-1.5, max_drawdown=65.5, 6 sign flips),
not all-zero. The degeneracy #19 saw was deep-SMA/param-dependent, not intrinsic
to the symbol. #19 to be re-scoped. Other findings: `aura run <junk>` exit 0
(already #16), `aura --help` lands on the exit-2 error path (friction), SimBroker
two-f64-slot order unchecked at bootstrap (documented footgun) — both filed.
This commit is contained in:
2026-06-04 22:25:01 +02:00
parent 77ad977623
commit 4fac529de0
6 changed files with 1458 additions and 0 deletions
@@ -0,0 +1,183 @@
# Fieldtest — milestone "Walking skeleton" — 2026-06-04
**Status:** Draft — awaiting orchestrator triage
**Author:** fieldtester (dispatched by fieldtest skill, milestone-scope variant)
**Scope mode:** MILESTONE (closing functional gate), not per-cycle.
## Scope
The "Walking skeleton" milestone (Gitea Brummel/Aura issues #4#8) promises a
runnable end-to-end thread (design ledger INDEX.md ~line 20): **ingest → one
signal (SMA-cross) → exposure/intent stream → sim-optimal broker → synthetic
pip-equity signal-quality metric**, foldable into a structured RunReport
(manifest + metrics, C18/C14), deterministic (C1), reachable via the `aura run`
CLI (#8), and — per C22 — populated enough that "a newcomer sees a populated
trace immediately."
This fieldtest derives three end-to-end scenarios top-down from that promise and
drives them from the **public interface only** (the `aura` binary's observable
behaviour, rustdoc, INDEX.md, public re-exports, data-server's public rustdoc).
No `crates/*/src` was read. The artefact under test was built from HEAD
(`cargo build -p aura-cli`; the consumer crate compiled against the current
path-dep'd engine crates).
## Examples
### fieldtests/milestone-walking-skeleton/ms_1_newcomer_cli.sh — the newcomer CLI path
- Invokes the real `target/debug/aura` binary (built from HEAD) as a downstream
user with nothing but the rustdoc module blurb. Checks: single-line JSON
RunReport with the documented `{manifest, metrics}` shape; exit 0;
byte-identical across two runs (C1); a NON-degenerate trace (≥1 exposure sign
flip, C22); and the bad-args / usage contract.
- Fits the promise: this is the one-command surface the milestone says makes the
whole thread runnable, and the C22 "populated trace" demonstrator.
- Outcome: built? yes. ran? yes. matched expected? **yes** — single-line
`{manifest,metrics}`, exit 0, byte-identical, metrics
`total_pips=-0.13, max_drawdown=0.17, exposure_sign_flips=1` (populated).
Bad/empty subcommand → exit 2, `aura: usage: aura run` on stderr, empty stdout.
### fieldtests/milestone-walking-skeleton/ms_2_real_data_thread.rs — the real-data thread
- A standalone consumer crate (own `[workspace]`, path-deps on
aura-core/std/engine/ingest + git dep on data-server — the shape a C16 project
takes) wires the WHOLE thread over real recorded M1 bars:
`load_m1_window(AAPL.US, 2006-08) → close_stream → SMA(2)/SMA(4) → Sub →
Exposure(0.5) → SimBroker(1e-4) → 2×Recorder → f64_field → summarize →
RunReport`, then folds the report twice and asserts determinism.
- Fits the promise: this is the literal ingest→…→metric thread over REAL bars,
the substance #7 + the milestone claim.
- Outcome: built? **yes, first try.** ran? yes (21 M1 bars ingested). matched
expected? **yes, and better than the carrier predicted** — the run is
deterministic (cross-process byte-identical RunReport, C1) AND **non-degenerate**:
`total_pips=-1.5, max_drawdown=65.5, exposure_sign_flips=6`. Window timestamps
are canonical epoch-ns. See finding [spec_gap] on the #19 premise.
### fieldtests/milestone-walking-skeleton/ms_3_epoch_ns_coherence.rs — epoch-ns coherence
- Hand-builds 4 `M1Parsed` bars (data-server's public AoS record) with known
`time_ms`, runs `transpose_m1 → close_stream → SMA(2) → Recorder`, and asserts
the recorded sink-trace timestamps equal `unix_ms_to_epoch_ns(time_ms)`
(= ms·1e6) — the C3 normalization carried intact from ingest through the run
loop into the recorded metric stream. No external data needed.
- Fits the promise: closes the loop on C3 ("normalize at the one boundary") —
proves the metric stream a newcomer reads is in aura's canonical time unit.
- Outcome: built? yes. ran? yes. matched expected? **yes** — ingest stamps
`[…400e9, …460e9, …520e9, …580e9]`; sink trace is the warmed suffix in the same
epoch-ns stamps.
## Findings
### [working] The full real-data thread is authorable from the public interface and is deterministic + populated
- Example: ms_2_real_data_thread.rs.
- What happened: the entire ingest→signal→exposure→broker→metrics→RunReport thread
compiled on the first attempt from rustdoc signatures alone (Harness::bootstrap,
Edge/Target/SourceSpec field layout, summarize/f64_field, the aura-std node
ctors, load_m1_window/close_stream). Two in-process folds and two separate
processes produced byte-identical RunReports.
- Why working: the milestone's headline promise — a real ingest→metric thread,
deterministic (C1) — is delivered, and the public surface is self-sufficient
for a downstream author to wire it. This is the milestone's central win.
- Recommended action: carry-on.
### [working] `aura run` delivers the C22 populated-trace newcomer path
- Example: ms_1_newcomer_cli.sh.
- What happened: one command, single-line `{manifest,metrics}` JSON, exit 0,
byte-identical twice, non-degenerate (1 sign flip, real drawdown). Bad/missing
subcommand → exit 2 + usage on stderr + empty stdout.
- Why working: C22 ("a newcomer sees a populated trace immediately") and C14
(structured face from a binary) are both satisfied by the shipped binary.
- Recommended action: carry-on.
### [working] C3 epoch-ns normalization is coherent end to end
- Example: ms_3_epoch_ns_coherence.rs.
- What happened: `close_stream` timestamps equal `unix_ms_to_epoch_ns(time_ms)`,
and the recorded sink trace carries exactly those epoch-ns stamps (warmed
suffix). ms·1e6 holds.
- Why working: C3's "one boundary normalizes, nowhere else" is observably true
through ingest → run → sink; the metric stream is in canonical time.
- Recommended action: carry-on.
### [spec_gap] The #19 "AAPL.US too sparse → degenerate metrics" premise does NOT hold for the SMA(2)/SMA(4) demo config
- Example: ms_2_real_data_thread.rs.
- What happened: the carrier (and issue #19, from the cycle-0011 per-cycle
fieldtest) asserts AAPL.US 2006 is too sparse (~21 M1 bars/month) to warm an
SMA-cross, so the real-data run yields all-zero degenerate metrics. **Empirically
this is false for the SMA(2)/SMA(4) cross** (the same fast/slow pair the shipped
`aura run` sample uses): 21 bars warm SMA(4) after 4 bars, the cross produces
**6 exposure sign flips**, `total_pips=-1.5`, `max_drawdown=65.5` — a populated,
non-degenerate trace over REAL bars. The degeneracy #19 observed is
**window/param-dependent** (a deeper SMA, e.g. the longer lengths a realistic
daily cross would use, would not warm on 21 bars), not intrinsic to the symbol.
- Why spec_gap: the milestone never pins the "real-data demo" SMA lengths or
window, so two equally-plausible readings diverge on whether the real-data path
is degenerate. I picked the shipped `aura run` config (SMA 2/4); under it the
real-data thread is **populated**, not degenerate. The honest milestone roll-up
therefore differs from the carrier's pre-stated assumption: the real-data thread
is *also* a populated-trace demonstrator at the shipped param config, not merely
"correct but degenerate." #19's concern remains valid for *deeper* SMAs / the
C22 "name a denser demo symbol" recommendation, but it is not a blocker and the
milestone promise is met on real bars at the demo config.
- Recommended action: ratify (record that the walking-skeleton real-data demo is
pinned to SMA 2/4 over 2006-08 and is populated there) AND re-scope #19 to
"denser demo data for *deep/daily* crosses" rather than "real path is
degenerate" — the latter is no longer accurate.
### [spec_gap] `aura run <trailing junk>` → exit 0, trailing args silently ignored
- Example: ms_1_newcomer_cli.sh (`aura run extra junk` → exit 0, full report).
- What happened: the arg parser keys only on the first token being `run` and
ignores everything after. `aura run garbage` runs the sample harness and exits 0.
- Why spec_gap: re-confirmation at the milestone gate of the cycle-0010 finding
(already filed). The milestone's CLI contract does not unambiguously cover
`run <extra>`; the binary chose the lenient reading. Not a regression — same
behaviour as cycle-0010.
- Recommended action: ratify-or-tighten (decide whether trailing args reject with
exit 2 or are deliberately ignored). Low priority; carried from #?/cycle-0010.
### [friction] `aura --help` lands on the error path (exit 2, bare usage line)
- Example: ms_1_newcomer_cli.sh (`aura --help` → exit 2, `aura: usage: aura run`
on stderr, empty stdout).
- What happened: a newcomer's reflex first command (`--help`) is treated as an
unknown subcommand: exit 2, no help text, just the one-line usage on stderr.
- Why friction: the task (discover how to run) completes — the usage line *does*
name `aura run` — but the conventional discovery affordance returns a nonzero
error path rather than help on stdout. For a "newcomer sees a populated trace"
milestone (C22), the first-contact ergonomics are slightly off.
- Recommended action: plan (tidy) — make `--help`/`-h` print the usage to stdout
with exit 0; keep unknown subcommands at exit 2. Trivial, optional.
### [friction] SimBroker slot order is load-bearing, both f64, unchecked at bootstrap
- Example: ms_2_real_data_thread.rs (had to consult rustdoc to wire slot 0 =
exposure, slot 1 = price correctly).
- What happened: SimBroker's two inputs are both `f64`, so a swapped wiring is not
caught at bootstrap (the rustdoc explicitly warns it "would silently produce a
wrong equity curve"). The downstream author must read prose to get it right;
there is no typed guard.
- Why friction: the thread is wireable correctly *only* by reading the rustdoc
carefully — a known, documented footgun, but a footgun. Not a bug (it is
documented and the wiring API offers no kind to distinguish the two f64 slots).
- Recommended action: plan (consider a named-role or distinct-newtype wiring for
broker inputs in a later cycle); or carry-on if deemed acceptable given the
rustdoc warning. Documented, so low urgency.
## Recommendation summary
| Finding | Class | Action |
|---|---|---|
| Real-data thread authorable, deterministic, populated | working | carry-on |
| `aura run` C22 populated-trace newcomer path | working | carry-on |
| C3 epoch-ns coherence ingest→run→sink | working | carry-on |
| #19 "real path degenerate" false at SMA 2/4 demo config | spec_gap | ratify + re-scope #19 |
| `aura run <junk>` → exit 0 (lenient) | spec_gap | ratify-or-tighten |
| `aura --help` → exit 2, no help text | friction | plan (tidy) |
| SimBroker slot order load-bearing, unchecked | friction | plan / carry-on |
## Milestone roll-up
**The "Walking skeleton" milestone DELIVERS its promise end to end.** The full
thread — ingest → SMA-cross → exposure → sim-optimal broker → pip-equity
metric → structured RunReport — runs deterministically (C1, cross-process
byte-identical), in canonical epoch-ns time (C3), reachable via `aura run`
(#8/C14), and produces a **populated** trace (C22) on **both** the synthetic
sample harness AND the real AAPL.US 2006-08 bars at the shipped SMA(2)/SMA(4)
config. The carrier's pre-stated worry — that the real-data path is correct-but-
degenerate — did not reproduce at the demo configuration; #19's degeneracy is a
deep-SMA / dense-data concern, not a milestone failure. No bugs found. Two
spec_gaps and two friction items, none blocking.
+938
View File
@@ -0,0 +1,938 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "aura-core"
version = "0.1.0"
[[package]]
name = "aura-engine"
version = "0.1.0"
dependencies = [
"aura-core",
]
[[package]]
name = "aura-ingest"
version = "0.1.0"
dependencies = [
"aura-core",
"data-server",
]
[[package]]
name = "aura-std"
version = "0.1.0"
dependencies = [
"aura-core",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bzip2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
dependencies = [
"bzip2-sys",
]
[[package]]
name = "bzip2-sys"
version = "0.1.13+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [
"find-msvc-tools",
"jobserver",
"libc",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "data-server"
version = "0.1.0"
source = "git+http://192.168.178.103:3000/Brummel/data-server.git?branch=main#0f5e6655b90eeab0f43a8b0f330b8d1632e80ed4"
dependencies = [
"chrono",
"regex",
"zip",
]
[[package]]
name = "deflate64"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2"
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]]
name = "derive_arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "displaydoc"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "flate2"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
"wasip2",
"wasm-bindgen",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "iana-time-zone"
version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "jobserver"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
"getrandom",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "log"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"byteorder",
"crc",
]
[[package]]
name = "lzma-sys"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "memchr"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
]
[[package]]
name = "ms-walking-skeleton-fieldtest"
version = "0.0.0"
dependencies = [
"aura-core",
"aura-engine",
"aura-ingest",
"aura-std",
"data-server",
]
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
]
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pkg-config"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "regex"
version = "1.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "simd-adler32"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasip2"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "xz2"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
dependencies = [
"lzma-sys",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zip"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
dependencies = [
"aes",
"arbitrary",
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils",
"deflate64",
"displaydoc",
"flate2",
"getrandom",
"hmac",
"indexmap",
"lzma-rs",
"memchr",
"pbkdf2",
"sha1",
"thiserror",
"time",
"xz2",
"zeroize",
"zopfli",
"zstd",
]
[[package]]
name = "zopfli"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
dependencies = [
"bumpalo",
"crc32fast",
"log",
"simd-adler32",
]
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.16+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
dependencies = [
"cc",
"pkg-config",
]
@@ -0,0 +1,31 @@
# Milestone fieldtest consumer crate — a standalone downstream project (as a C16
# project would be): path-deps on the engine crates + a cargo git dep on
# data-server, exactly the shape aura-ingest's own manifest takes.
#
# It drives the full walking-skeleton thread from the PUBLIC interface only:
# load_m1_window -> close_stream -> SMA-cross -> Exposure -> SimBroker
# -> Recorder sinks -> f64_field -> summarize -> RunReport.
# Empty [workspace] table: marks this fixture crate as its OWN workspace root,
# so it does not become a member of the aura engine workspace.
[workspace]
[package]
name = "ms-walking-skeleton-fieldtest"
edition = "2024"
version = "0.0.0"
publish = false
[[bin]]
name = "real_data_thread"
path = "ms_2_real_data_thread.rs"
[[bin]]
name = "coherence"
path = "ms_3_epoch_ns_coherence.rs"
[dependencies]
aura-core = { path = "../../crates/aura-core" }
aura-std = { path = "../../crates/aura-std" }
aura-engine = { path = "../../crates/aura-engine" }
aura-ingest = { path = "../../crates/aura-ingest" }
data-server = { git = "http://192.168.178.103:3000/Brummel/data-server.git", branch = "main" }
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Milestone fieldtest — scenario 1: THE NEWCOMER CLI PATH
#
# A downstream user with nothing but the public docs (aura's rustdoc module
# blurb: "aura run bootstraps a built-in sample signal-quality harness ...
# prints the run's metrics + manifest as canonical JSON to stdout") invokes the
# real binary and checks the milestone promise end to end:
# - a single-line JSON RunReport with the documented {manifest, metrics} shape
# - exit 0
# - byte-identical across two runs (C1 determinism)
# - a NON-degenerate populated trace (C22 "newcomer sees a populated trace")
# - the bad-args / usage contract
#
# Run from the repo root. Builds from HEAD first (never a stale artifact).
set -u
cd "$(git rev-parse --show-toplevel)"
cargo build -q -p aura-cli || { echo "BUILD FAILED"; exit 1; }
BIN=target/debug/aura
fail=0
A=$("$BIN" run); ea=$?
B=$("$BIN" run); eb=$?
echo "report: $A"
[ "$ea" -eq 0 ] && [ "$eb" -eq 0 ] || { echo "FAIL: run exit $ea/$eb (want 0)"; fail=1; }
[ "$A" = "$B" ] || { echo "FAIL: two runs differ (C1)"; fail=1; }
# single line: no embedded newline
[ "$(printf '%s' "$A" | wc -l)" -eq 0 ] || { echo "FAIL: not single-line"; fail=1; }
# documented shape
case "$A" in
*'"manifest"'*'"metrics"'*'"total_pips"'*'"max_drawdown"'*'"exposure_sign_flips"'*) ;;
*) echo "FAIL: missing documented {manifest,metrics} keys"; fail=1;;
esac
# NON-degenerate: at least one of the metrics is non-zero (a populated trace).
# The synthetic sample stream rises then reverses -> 1 sign flip, real drawdown.
case "$A" in
*'"exposure_sign_flips":0'*) echo "FAIL: degenerate trace (0 sign flips)"; fail=1;;
esac
# bad-args contract: no subcommand and unknown subcommand -> exit 2, usage on
# stderr, empty stdout.
"$BIN" >/tmp/ms1_o 2>/tmp/ms1_e; e=$?
[ "$e" -eq 2 ] || { echo "FAIL: no-arg exit $e (want 2)"; fail=1; }
[ -s /tmp/ms1_o ] && { echo "FAIL: no-arg wrote stdout"; fail=1; }
grep -q usage /tmp/ms1_e || { echo "FAIL: no usage on stderr"; fail=1; }
"$BIN" frobnicate >/tmp/ms1_o 2>/tmp/ms1_e; e=$?
[ "$e" -eq 2 ] || { echo "FAIL: bad-subcmd exit $e (want 2)"; fail=1; }
# OBSERVED ANOMALY (recorded, not asserted): `aura run extra junk` -> exit 0,
# trailing args silently ignored. Documented as a spec_gap in the spec.
"$BIN" run extra junk >/tmp/ms1_o 2>/dev/null; echo "run+junk exit: $? (observed lenient)"
[ "$fail" -eq 0 ] && echo "SCENARIO 1: PASS" || echo "SCENARIO 1: FAIL"
exit "$fail"
@@ -0,0 +1,160 @@
//! Milestone fieldtest — scenario 2: THE REAL-DATA THREAD.
//!
//! A downstream author wires the WHOLE walking-skeleton promise over REAL
//! recorded M1 bars, using only the public interface:
//!
//! data-server -> aura_ingest::load_m1_window (ingest, C3 epoch-ns)
//! -> M1Columns::close_stream (SoA close column, C7)
//! -> SMA(fast) / SMA(slow) -> Sub (the SMA-cross signal)
//! -> Exposure (intent/exposure stream, C10)
//! -> SimBroker (sim-optimal pip equity, C10)
//! -> Recorder x2 (sink trace, C8/C22)
//! -> f64_field + summarize (RunMetrics, C12)
//! -> RunReport { manifest, metrics }(structured face, C14/C18)
//!
//! Then asserts determinism (C1): two independent runs over the same window
//! fold byte-identical RunReports.
//!
//! Window: AAPL.US, 2006-08 (the symbol/window the milestone shipped its
//! integration test against). Skips cleanly if /mnt/tickdata is absent.
use std::sync::mpsc;
use aura_core::{Firing, Scalar, ScalarKind, Timestamp};
use aura_engine::{summarize, f64_field, Edge, Harness, RunManifest, RunMetrics, RunReport, SourceSpec, Target};
use aura_ingest::{load_m1_window, M1Columns};
use aura_std::{Exposure, Recorder, SimBroker, Sma};
use data_server::{DataServer, DEFAULT_DATA_PATH};
const SYMBOL: &str = "AAPL.US";
// 2006-08-01 .. 2006-09-01 UTC, inclusive Unix-ms (data-server's contract).
const FROM_MS: i64 = 1_154_390_400_000;
const TO_MS: i64 = 1_157_068_800_000;
const SMA_FAST: usize = 2;
const SMA_SLOW: usize = 4;
const EXPOSURE_SCALE: f64 = 0.5;
const PIP_SIZE: f64 = 0.0001;
/// Fold the recorded equity+exposure traces of one run into a RunReport.
fn run_once(prices: &[(Timestamp, Scalar)]) -> RunReport {
// Node array (indices are the wiring vocabulary):
// 0 SMA(fast) 1 SMA(slow) 2 Sub 3 Exposure
// 4 SimBroker 5 Recorder(equity) 6 Recorder(exposure)
let (eq_tx, eq_rx) = mpsc::channel::<(Timestamp, Vec<Scalar>)>();
let (ex_tx, ex_rx) = mpsc::channel::<(Timestamp, Vec<Scalar>)>();
let nodes: Vec<Box<dyn aura_core::Node>> = vec![
Box::new(Sma::new(SMA_FAST)),
Box::new(Sma::new(SMA_SLOW)),
Box::new(aura_std::Sub::new()),
Box::new(Exposure::new(EXPOSURE_SCALE)),
Box::new(SimBroker::new(PIP_SIZE)),
Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, eq_tx)),
Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, ex_tx)),
];
// One source: the close price. It feeds SMA(fast) slot 0, SMA(slow) slot 0,
// and SimBroker slot 1 (price).
let sources = vec![SourceSpec {
kind: ScalarKind::F64,
targets: vec![
Target { node: 0, slot: 0 },
Target { node: 1, slot: 0 },
Target { node: 4, slot: 1 },
],
}];
let edges = vec![
// Sub = SMA_fast - SMA_slow
Edge { from: 0, to: 2, slot: 0, from_field: 0 },
Edge { from: 1, to: 2, slot: 1, from_field: 0 },
// Exposure <- Sub
Edge { from: 2, to: 3, slot: 0, from_field: 0 },
// SimBroker slot 0 = exposure
Edge { from: 3, to: 4, slot: 0, from_field: 0 },
// equity sink <- SimBroker
Edge { from: 4, to: 5, slot: 0, from_field: 0 },
// exposure sink <- Exposure
Edge { from: 3, to: 6, slot: 0, from_field: 0 },
];
let mut harness = Harness::bootstrap(nodes, sources, edges).expect("bootstrap");
harness.run(vec![prices.to_vec()]);
drop(harness); // close the senders so the receivers drain to completion
let equity_rows: Vec<(Timestamp, Vec<Scalar>)> = eq_rx.iter().collect();
let exposure_rows: Vec<(Timestamp, Vec<Scalar>)> = ex_rx.iter().collect();
let equity = f64_field(&equity_rows, 0);
let exposure = f64_field(&exposure_rows, 0);
let metrics: RunMetrics = summarize(&equity, &exposure);
let window = (
prices.first().map(|p| p.0).unwrap_or(Timestamp(0)),
prices.last().map(|p| p.0).unwrap_or(Timestamp(0)),
);
RunReport {
manifest: RunManifest {
commit: "fieldtest".into(),
params: vec![
("sma_fast".into(), SMA_FAST as f64),
("sma_slow".into(), SMA_SLOW as f64),
("exposure_scale".into(), EXPOSURE_SCALE),
],
window,
seed: 0,
broker: format!("sim-optimal(pip_size={PIP_SIZE})"),
},
metrics,
}
}
fn main() {
if !std::path::Path::new(DEFAULT_DATA_PATH).exists() {
eprintln!("SKIP: {DEFAULT_DATA_PATH} absent");
return;
}
let server = std::sync::Arc::new(DataServer::new(DEFAULT_DATA_PATH));
if !server.has_symbol(SYMBOL) {
eprintln!("SKIP: data-server has no symbol {SYMBOL}");
return;
}
let cols: M1Columns = match load_m1_window(&server, SYMBOL, FROM_MS, TO_MS) {
Some(c) => c,
None => {
eprintln!("SKIP: no data in window for {SYMBOL}");
return;
}
};
let prices = cols.close_stream();
eprintln!("ingested {} M1 close bars over [{FROM_MS},{TO_MS}]", prices.len());
if let (Some(first), Some(last)) = (prices.first(), prices.last()) {
eprintln!("first ts (epoch-ns): {:?} last ts: {:?}", first.0, last.0);
}
let r1 = run_once(&prices);
let r2 = run_once(&prices);
let j1 = r1.to_json();
let j2 = r2.to_json();
println!("RunReport: {j1}");
// C1: determinism.
assert_eq!(j1, j2, "two runs over identical input must fold identical reports (C1)");
// Validity: metrics are finite (degenerate-all-zero is valid per C2 warm-up).
assert!(r1.metrics.total_pips.is_finite(), "total_pips finite");
assert!(r1.metrics.max_drawdown.is_finite() && r1.metrics.max_drawdown >= 0.0, "drawdown finite, >=0");
let m: &RunMetrics = &r1.metrics;
let degenerate = m.total_pips == 0.0 && m.max_drawdown == 0.0 && m.exposure_sign_flips == 0;
eprintln!(
"metrics: total_pips={} max_drawdown={} sign_flips={} ({})",
m.total_pips,
m.max_drawdown,
m.exposure_sign_flips,
if degenerate { "DEGENERATE (valid)" } else { "POPULATED" }
);
println!("SCENARIO 2: PASS (deterministic, finite{})", if degenerate { ", degenerate" } else { ", populated" });
}
@@ -0,0 +1,90 @@
//! Milestone fieldtest — scenario 3: EPOCH-NS COHERENCE END TO END.
//!
//! Ties C3's one unit normalization (Unix-ms -> canonical epoch-ns) from the
//! ingestion boundary all the way through to the recorded sink trace that the
//! metric stream is folded from. The promise under test: the timestamps a
//! newcomer reads off a recorded trace are aura's canonical epoch-ns, derived
//! once at ingestion and carried unchanged through the run loop.
//!
//! Source records are hand-built `M1Parsed` (data-server's public AoS record —
//! exactly the shape a recorder / live-source edge materializes), with known
//! `time_ms`. We assert:
//! 1. close_stream timestamps == unix_ms_to_epoch_ns(time_ms) for each bar
//! (C3: the boundary normalizes; ms * 1_000_000).
//! 2. the Recorder sink trace timestamps (captured during the run) equal the
//! same epoch-ns values — the normalization survives ingest -> run -> sink.
//!
//! No external data needed: this is a pure-construction coherence check.
use std::sync::mpsc;
use aura_core::{Firing, Scalar, ScalarKind, Timestamp};
use aura_engine::{Edge, Harness, SourceSpec, Target};
use aura_ingest::{transpose_m1, unix_ms_to_epoch_ns};
use aura_std::{Recorder, Sma};
use data_server::records::M1Parsed;
fn bar(time_ms: i64, close: f64) -> M1Parsed {
M1Parsed { time_ms, open: close, high: close, low: close, close, spread: 0.0, volume: 1 }
}
fn main() {
// Known minute timestamps (arbitrary but realistic Unix-ms, one minute apart).
let times_ms = [1_154_390_400_000_i64, 1_154_390_460_000, 1_154_390_520_000, 1_154_390_580_000];
let bars: Vec<M1Parsed> = times_ms
.iter()
.enumerate()
.map(|(i, &t)| bar(t, 1.0 + i as f64))
.collect();
// INGEST: AoS -> SoA, normalizing time at the one boundary (C3).
let cols = transpose_m1(&bars);
let prices = cols.close_stream();
// (1) close_stream carries epoch-ns, not Unix-ms.
let expected_ns: Vec<Timestamp> = times_ms.iter().map(|&t| unix_ms_to_epoch_ns(t)).collect();
let got_ns: Vec<Timestamp> = prices.iter().map(|p| p.0).collect();
assert_eq!(got_ns, expected_ns, "close_stream must carry canonical epoch-ns (C3)");
// sanity: epoch-ns == ms * 1_000_000
for (&ms, ns) in times_ms.iter().zip(&expected_ns) {
assert_eq!(ns.0, ms * 1_000_000, "epoch-ns is ms*1e6");
}
eprintln!("ingest ts: {:?} (epoch-ns)", got_ns);
// RUN: price -> SMA(2) -> Recorder. We do not need the full strategy here;
// a one-node-plus-sink graph is enough to prove the timestamp carries
// through the run loop into the recorded trace.
let (tx, rx) = mpsc::channel::<(Timestamp, Vec<Scalar>)>();
let nodes: Vec<Box<dyn aura_core::Node>> = vec![
Box::new(Sma::new(2)), // 0
Box::new(Recorder::new(&[ScalarKind::F64], Firing::Any, tx)), // 1
];
let sources = vec![SourceSpec {
kind: ScalarKind::F64,
targets: vec![Target { node: 0, slot: 0 }],
}];
let edges = vec![Edge { from: 0, to: 1, slot: 0, from_field: 0 }];
let mut harness = Harness::bootstrap(nodes, sources, edges).expect("bootstrap");
harness.run(vec![prices.clone()]);
drop(harness);
let recorded: Vec<(Timestamp, Vec<Scalar>)> = rx.iter().collect();
let recorded_ts: Vec<Timestamp> = recorded.iter().map(|r| r.0).collect();
eprintln!("sink ts: {:?} (epoch-ns)", recorded_ts);
// (2) Every recorded timestamp is one of the ingested epoch-ns stamps
// (SMA(2) warms after the first bar, so the sink trace is a suffix of the
// ingested timeline — but every stamp it carries is the canonical epoch-ns).
assert!(!recorded_ts.is_empty(), "sink recorded at least one warmed cycle");
for ts in &recorded_ts {
assert!(
expected_ns.contains(ts),
"recorded sink ts {ts:?} is not a canonical ingested epoch-ns stamp"
);
}
// and they are strictly the warmed suffix, in order
assert_eq!(recorded_ts, expected_ns[1..].to_vec(), "sink trace = warmed suffix, in epoch-ns order");
println!("SCENARIO 3: PASS (epoch-ns coherent ingest -> run -> sink)");
}