Files
claude 14c43474ab fieldtest: construction-args — 4 examples, 0 bugs / 1 friction / 2 spec-gap / 4 working
Source-blind consumer run over the #271 surface: a NY first-bar-
momentum Session authored purely as data and run e2e over --real
US500; a LinComb{arity:3} blend binding the arity-unlocked weights
with the Session period swept (4 members, reproduced 4/4
bit-identically); eight strict-form refusal probes (all exit 1,
actionable prose); an args composite registered and use-spliced into
a consumer (v2 propagates, ids stable, C29 doc gate holds).

Findings routed: Count lexical form + asymmetry ratified into
glossary/guide (follow-up commit); introspect dead-end on arg-bearing
ports and the missing CostSum wiring path absorbed into #341.

refs #271
2026-07-24 22:05:34 +02:00

218 lines
9.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fieldtest transcript — cycle #271 (typed construction args)
Verbatim command/output capture for the `fieldtests/construction-args/` fixtures.
Binary: `target/debug/aura` built from HEAD `6ca359a` via `cargo build --workspace`
(debug profile). Runs used a scratch project `aura new ca_lab` (in `/tmp/ca_lab`);
real data resolved from the built-in archive (`/mnt/tickdata/Pepperstone`). All
op-scripts authored from the public interface only (README, authoring guide,
glossary, design ledger C24, `aura graph introspect`).
Public interface only — no crate source read.
---
## Discovery loop (axis 3) — is the args grammar learnable from the binary?
```
$ aura graph introspect --vocabulary | wc -l
36
$ aura graph introspect --node Session
Session — bars elapsed since the session open, from the configured open time and timezone
arg tz: Tz (IANA timezone name, e.g. Europe/Berlin)
arg open: TimeOfDay (local wall-clock HH:MM)
note ports and params form at construction; args are required
$ aura graph introspect --node LinComb
LinComb — linear combination of its inputs with constant weights
arg arity: Count (positive integer count)
note ports and params form at construction; args are required
$ aura graph introspect --node CostSum
CostSum — sums cost-model contributions into one cost-in-R stream
arg n_costs: Count (positive integer count)
note ports and params form at construction; args are required
```
The arg *grammar* (name, kind, hint) is fully self-describing. But `--node <T>`
stops at the note — it does NOT reveal the post-construction ports/params. To
learn the port names you must author a partial op-list carrying the args and pipe
it through `--unwired`; to learn the param names you must build and `--list-axes`.
Nothing on the `--node` surface names those follow-up steps. First naive guess at
LinComb's input port was `lc.in0` — wrong:
```
$ echo '[{"op":"add","type":"LinComb","name":"lc","args":{"arity":"3"}}]' \
| aura graph introspect --unwired
lc.term[0]:F64
lc.term[1]:F64
lc.term[2]:F64
$ echo '[{"op":"add","type":"CostSum","name":"cs","args":{"n_costs":"3"}}]' \
| aura graph introspect --unwired
cs.cost[0].cost_in_r:F64
cs.cost[0].cum_cost_in_r:F64
cs.cost[0].open_cost_in_r:F64
cs.cost[1].cost_in_r:F64 (… ×3 slots, 3 fields each)
```
Weight param names, discovered only after a build:
```
$ aura graph build < lc_probe.ops.json > lc_probe.bp.json
$ aura sweep lc_probe.bp.json --list-axes
graph.lc.weights[0]:F64 # open by default → sweepable (C24)
graph.lc.weights[1]:F64
...
```
---
## Example 1 — `ca_1_ny_session` (axis 1: non-Frankfurt Session as data)
```
$ aura graph build < ca_1_ny_session.ops.json > ca_1_ny_session.bp.json
$ head -c 25 ca_1_ny_session.bp.json
{"format_version":2,"blue # arg-bearing → v2
$ aura graph introspect --content-id --identity-id < ca_1_ny_session.ops.json
5b085a5b3f411072e48d4b54717b3d09e88be50b0bd797d345c2ee90baa41c37 # content id
4bf0513a4d2e852ab580fd8846737f57c1825b944ff9df1a48040a07fe92446b # identity id
# --- inside ca_lab/ ---
$ aura run ca_1_ny_session.bp.json --real US500 --from 1725148800000 --to 1725580800000
{"manifest":{... "topology_hash":"5b085a5b...b40d95cf"? -> 5b085a5b... (== content id) ...},
"metrics":{... "expectancy_r":0.7853745294474523,"n_trades":18 ...}} (exit 0)
# synthetic refuses cleanly (OHLC strategy, close-only synthetic stream):
$ aura run ca_1_ny_session.bp.json
aura: strategy "graph" consumes columns beyond close (open, high, low) —
synthetic data generates a close series only; run with --real <SYMBOL>
```
Full report captured in `ca_1_ny_session.run.json`.
---
## Example 2 — `ca_2_lincomb_blend` (axis 2: LinComb arity + weights + swept period)
Three SMA spreads blended through `LinComb(arity 3)` with bound `weights[i]`,
NY-session-gated, `Session.period_minutes` left OPEN (sweepable).
```
$ aura graph build < ca_2_lincomb_blend.ops.json > ca_2_lincomb_blend.bp.json
$ head -c 25 ca_2_lincomb_blend.bp.json
{"format_version":2,"blue
$ aura sweep ca_2_lincomb_blend.bp.json --list-axes
graph.sess.period_minutes:I64 # open
graph.lc.weights[0]:F64 default=0.5 # bound (re-openable by --axis)
graph.lc.weights[1]:F64 default=0.3
graph.lc.weights[2]:F64 default=0.2
...
# --- inside ca_lab/ --- sweep the arity-unlocked weight AND the period param:
$ aura sweep ca_2_lincomb_blend.bp.json --real US500 --from 1725148800000 --to 1725580800000 \
--axis graph.sess.period_minutes=15,30 --axis 'graph.lc.weights[0]=0.3,0.5' --name ca2_blend
{"family_id":"caa3508a-0-US500-w0-r0-s0-0", ... 4 members ...} (exit 0)
$ aura reproduce caa3508a-0-US500-w0-r0-s0-0
... member graph.lc.weights[0]=0.3, graph.sess.period_minutes=15 ... reproduced: bit-identical
... (×4)
reproduced 4/4 members bit-identically
```
Sweep output in `ca_2_lincomb_blend.sweep.jsonl`. NB: at this gating, the two
`weights[0]` values yield identical member metrics (the gated blend's sign is
weight-insensitive here) — a semantic property of the strategy, not the arg
channel; the params bound and swept correctly.
CostSum arity probe: `ca_2b_costsum_arity.ops.json``--unwired` shows
`cost[i].{cost_in_r,cum_cost_in_r,open_cost_in_r}` (3 slots × 3 fields). Wiring
CostSum end to end needs the RiskExecutor context (cost nodes take
`closed/open/entry_price/stop_price`) — no op-script path for that exists in the
public corpus (see finding SG2).
---
## Example 3 — refusal battery (axis 4: strict-form refusal prose)
Every case: `aura graph build < FIXTURE`, stderr + exit code.
```
ca_3a_bad_tz aura: op 1 (add): node sess arg "tz" expects Tz (IANA timezone name, e.g. Europe/Berlin) — got "berlin" exit 1
ca_3b_unpadded_time aura: op 1 (add): node sess arg "open" expects TimeOfDay (local wall-clock HH:MM) — got "9:30" exit 1
ca_3c_zero_count aura: op 2 (add): node lc arg "arity" expects Count (positive integer count) — got "0" exit 1
ca_3d_garbage_count aura: op 1 (add): node lc arg "arity" expects Count (positive integer count) — got "three" exit 1
ca_3e_missing_arg aura: op 1 (add): node sess is missing required arg "tz" exit 1
ca_3f_args_on_argless aura: op 1 (add): node a takes no construction args exit 1
ca_3g_unknown_arg_key aura: op 1 (add): node sess has no construction arg "region" exit 1
ca_3h_partial_args aura: op 1 (add): node sess is missing required arg "open" exit 1
```
Edge-form strictness (ad-hoc, `--unwired`):
```
Count arity: "3"→ok "03"→refuse "+3"→refuse "3.0"→refuse "1e1"→refuse "-2"→refuse " 3"→refuse "0x3"→refuse
Time open: "09:30"→ok "00:00"→ok "23:59"→ok "24:00"→refuse "09:60"→refuse "9:05"→refuse "09:5"→refuse "09:30:00"→refuse "0930"→refuse
```
Note the padding asymmetry: `open` REQUIRES a zero-padded hour (`09:05`), but
`arity` REFUSES a leading zero (`03`). See finding SG1.
---
## Example 4 — `ca_4` register → use (axis 5: #317 interplay)
```
$ aura graph build < ca_4_ny_anchor_pattern.ops.json > ca_4_ny_anchor_pattern.bp.json
$ head -c 25 ca_4_ny_anchor_pattern.bp.json
{"format_version":2,"blue
$ aura graph introspect --content-id --identity-id < ca_4_ny_anchor_pattern.ops.json
d51132463b2d1780b3ed9b83dc9f9a229a9d6b93fce3e15c7c0572970e2e4564
863c89ddeaa2dfe7d79c7e55944263145399c67397a27c0955bdd768cc6ed72e
# --- inside ca_lab/ ---
$ aura graph register .../ca_4_ny_anchor_pattern.bp.json --name ny_anchor
registered blueprint d51132463b2d... (.../runs/blueprints/d51132463b2d....json)
label "ny_anchor" -> d51132463b2d...
$ aura graph introspect --registered
ny_anchor d51132463b2d NY cash-open session anchor: bars elapsed since the 09:30 America/New_York open
$ aura graph build < ca_4_consumer.ops.json > consumer.bp.json
# stderr: aura: note: use "anchor": ny_anchor -> d51132463b2d...
$ head -c 25 consumer.bp.json
{"format_version":2,"blue # v2 propagates THROUGH the splice
$ aura graph introspect --content-id --identity-id < ca_4_consumer.ops.json
985991f62cc6b92895018312c5ea86e493ef1c09dd53cf6e0813551baa97b671
bbe7060f4136255e594e1b01242ae80c3f5decfc543f23198d66adff6ce52a7b
$ aura run consumer.bp.json --real US500 --from 1725148800000 --to 1725580800000
{"manifest":{... "topology_hash":"985991f6..." (== content id),
"defaults":[["graph.anchor.sess.period_minutes",{"I64":15}], ...] ...},
"metrics":{... "n_trades":8 ...}} (exit 0)
# doc gate on the args-bearing composite (doc stripped):
$ aura graph register ca4_docless.bp.json --name ny_docless
aura: blueprint: composite `graph` carries no doc — a registered composite
describes itself (C29); add a doc line (...) before register exit 1
```
Consumer report captured in `ca_4_consumer.run.json`.
---
## Cross-cutting confirmations
```
# args-free document stays format_version 1 (byte-stability invariant, C18):
$ echo '[{"op":"source",...},{"op":"add","type":"SMA",...},...]' | aura graph build | head -c 25
{"format_version":1,"blue
# content id deterministic across builds:
$ aura graph introspect --content-id < ca_1_ny_session.ops.json (×2) -> identical
5b085a5b3f411072e48d4b54717b3d09e88be50b0bd797d345c2ee90baa41c37
5b085a5b3f411072e48d4b54717b3d09e88be50b0bd797d345c2ee90baa41c37
```