Files
Aura/fieldtests/m37-data-authorability-boundary/TRANSCRIPT.md
T
claude 697d81dd22 fieldtest: m37 data-authorability-boundary — 3 scenarios, 0 bugs / 2 friction / 1 spec-gap / 4 working
Milestone-close gate for milestone 37 (data-authorability boundary,
#310/#317/#271): source-blind consumer fieldtest against the release
binary at eb2b0a1, scenarios derived top-down from the milestone
promise rather than per-cycle axes.

- m37_1: NY Session authored via args + fold-selected taps, run e2e
  over US500 (default and --tap plans; summaries correct, unlisted
  tap inert).
- m37_2: London anchor registered by name, spliced via use into a
  consumer with open SMA lengths, swept over GER40, reproduced 2/2
  bit-identical (v2 propagates through the splice).
- m37_3a-e: five-way boundary-refusal battery; all exit 1 with
  actionable prose, escalation path named for namespaced types.

Gate verdict: green — all three boundary mechanisms deliver the
promise; genuinely-new logic is turned away toward Rust. Findings
routed: fold-summary read-back absorbed into #309, cross-run tap-file
lifecycle evidence onto #311, bare-refusal escalation-pointer parity
onto #341 (refs #309, refs #311, refs #341).
2026-07-24 23:43:53 +02:00

6.5 KiB
Raw Blame History

Fieldtest transcript — milestone-37 (data-authorability boundary)

Milestone-scope field test: the three data-authorability sites as ONE downstream story — fold selection as run-mode data (#310), a registered composite spliced by name (#317), construction args configuring non-scalar structure (#271) — and the boundary refusing what stays Rust-only.

Binary: target/release/aura, built from HEAD eb2b0a1 via cargo build --workspace --release (Finished in 5.53s, incremental — HEAD builds clean). Scratch project aura new m37_lab in /tmp/m37_lab; real data from the built-in Pepperstone archive. Public interface only (README, authoring-guide, glossary, design ledger, aura … --help, graph introspect) — no crate source read.


Scenario 1 — m37_1_ny_momentum — Session args + fold-selected taps, e2e

A NY first-session-bar momentum strategy authored purely as data: Session configured to America/New_York / 09:30 through the args channel, gating a SMA(5)SMA(20) sign onto the first 15m session bar. Four declared taps (spread, first_bar, gated_signal, slow_ma).

$ aura graph build < m37_1_ny_momentum.ops.json > m37_1_ny_momentum.bp.json
$ head -c 25 m37_1_ny_momentum.bp.json
{"format_version":2,"blue                  # arg-bearing Session -> v2
$ aura graph introspect --params m37_1_ny_momentum.ops.json
                                           # (empty = closed, runnable)
$ aura graph introspect --content-id --identity-id < m37_1_ny_momentum.ops.json
c8e123f0a8948034592eb5b15f32e9a3d6b0705c4c942e7563f655faa0d1e9f1
31de7ebd09122b9cef690618dbbdc08257056745b948840734982cd62a611265

# default plan (record all four taps):
$ aura run m37_1_ny_momentum.bp.json --real US500 --from 1725148800000 --to 1725580800000
{"manifest":{... "topology_hash":"c8e123f0..." ...},"metrics":{"total_pips":-2.099...,
 "r":{"expectancy_r":0.1566...,"n_trades":10, ...}}}          (exit 0, no stderr)

# fold-selected plan: spread=mean, first_bar=count, gated_signal=last; slow_ma unlisted
$ aura run m37_1_ny_momentum.bp.json --real US500 --from 1725148800000 --to 1725580800000 \
    --tap spread=mean --tap first_bar=count --tap gated_signal=last
{"manifest":{...IDENTICAL manifest+metrics as the default run...}}   (exit 0)
# stderr: aura: note: declared tap "slow_ma" unbound this run

The fold summary rows (the reason to pick a fold over record) land ONLY in the trace store, not on the run's stdout (see m37_1_fold_summary_rows.txt):

runs/traces/graph/index.json  "taps":["spread","first_bar","gated_signal"]  # slow_ma excluded
spread.json        {"tap":"spread","kinds":["F64"],"ts":[...],"columns":[[-0.2033544...]]}   # mean
first_bar.json     {"tap":"first_bar","kinds":["I64"],"ts":[...],"columns":[[5239.0]]}       # count = warm rows
gated_signal.json  {"tap":"gated_signal","kinds":["F64"],"ts":[...],"columns":[[0.0]]}       # last

aura run prints only manifest+metrics; aura chart graph emits HTML (exit 0), index-gated (the stale slow_ma.json from the default run lingers on disk but is NOT in the index and does NOT leak into the chart). There is no CLI verb that prints a fold's scalar summary as text. (Findings F1, SG1.)


Scenario 2 — m37_2 — registered composite spliced by name + args, swept

A London-open session anchor (open input pattern, SessionEurope/London / 08:00 via args) registered under a label, then spliced by name into a swept consumer.

$ aura graph build < m37_2_london_anchor.ops.json > m37_2_london_anchor.bp.json   # v2
$ aura graph register m37_2_london_anchor.bp.json --name london_anchor
registered blueprint 2deeddd943c9... 
label "london_anchor" -> 2deeddd943c9...
$ aura graph introspect --registered
london_anchor  2deeddd943c9  London cash-open session anchor: bars elapsed since the 08:00 Europe/London open

$ aura graph build < m37_2_consumer.ops.json > m37_2_consumer.bp.json
# stderr: aura: note: use "anchor": london_anchor -> 2deeddd943c9...
$ head -c 25 m37_2_consumer.bp.json
{"format_version":2,"blue                  # v2 propagates THROUGH the splice
$ aura graph introspect --content-id --identity-id < m37_2_consumer.ops.json
38369f968b9c10e6a56bca6ac1c347955492e4f58b125510cb09f4609502ad46
f964f3b0dadb8d04e73696bc61119222755c7feef81c55e35e5b85b91bcee7ec

$ aura sweep m37_2_consumer.bp.json --list-axes
graph.fast.length:I64
graph.slow.length:I64
graph.anchor.sess.period_minutes:I64 default=15     # spliced-instance param, path-qualified
...
$ aura sweep m37_2_consumer.bp.json --real GER40 --from 1725148800000 --to 1725580800000 \
    --axis graph.fast.length=5,10 --axis graph.slow.length=20 --name m37_london
# 2 members, exit 0
$ aura reproduce d5259c94-0-GER40-w0-r0-s0-0
... member graph.fast.length=5,  ... reproduced: bit-identical
... member graph.fast.length=10, ... reproduced: bit-identical
reproduced 2/2 members bit-identically

Everything worked first try. (Finding W3.)


Scenario 3 — the boundary refuses what stays Rust-only

# genuinely-new logic as a namespaced type -> refused, NAMES the Rust escalation verb
$ aura graph build < m37_3a_new_logic.ops.json
aura: op 2 (add): unknown node type "my_lab::ThirdCandle" — type id looks
      project-namespaced but this project binds no node crate — attach one
      with `aura nodes new <name>`                                          (exit 1)

# same, but bare (un-namespaced) type -> refused, but NO escalation pointer
$ echo '[...{"op":"add","type":"ThirdCandle",...}...]' | aura graph build
aura: op 1 (add): unknown node type "ThirdCandle"                          (exit 1)   # F2

# arg-bearing type entered half-configured -> MissingArg
$ aura graph build < m37_3b_session_no_args.ops.json
aura: op 2 (add): node sess is missing required arg "tz"                    (exit 1)

# construction args on an arg-less type -> refused
$ aura graph build < m37_3c_args_on_argless.ops.json
aura: op 2 (add): node fast takes no construction args                     (exit 1)

# a fold that is not a registered fold -> roster-enumerating refusal
$ aura run m37_1_ny_momentum.bp.json --real US500 --from ... --to ... --tap spread=median
aura: unknown fold 'median' — available: count, first, last, max, mean, min, record, sum   (exit 1)

# splice a composite that was never registered -> refused, enumerates labels
$ aura graph build < m37_3e_unknown_label.ops.json
aura: op 2 (use "anchor"): no registered blueprint labeled "tokyo_anchor"
      — registered labels: london_anchor                                    (exit 1)

Every boundary holds: exit 1, actionable prose. (Finding W4.)