Files
Aura/docs/specs/fieldtest-milestone-construction-layer.md
T
Brummel 7e195f66b7 fieldtest: milestone construction-layer — 4 examples, 7 findings
End-to-end milestone fieldtest (closing gate) for the "Construction layer"
milestone (#12 composites + #13 aura graph render). Four real downstream tasks
authored against the PUBLIC interface only (ledger + doc-comments + re-exports
+ the aura CLI), under fieldtests/milestone-construction-layer/:
  mc_1 — author a named sma_cross composite, build a Blueprint, compile +
         bootstrap + run (12 populated equity rows);
  mc_2 — correct vs fast/slow-swapped cross: labels + graph-as-data differ
         observably (the headline mis-wire-visible property);
  mc_3 — composite-nested-in-composite inlines and runs;
  mc_4 — walk the built graph via the read-only accessors, no engine internals.

Roll-up: friction_found, NO bugs. The milestone delivers its core promise —
fractal authoring -> compile -> bootstrap -> run, introspection as graph-as-data,
and param-carrying labels that make a mis-wire readable. None of the findings
block the gate.

Findings filed to the forward queue (not fixed here):
- #28 (feature) — the headline friction: `aura graph` renders only the built-in
  sample and the render adapter is CLI-private, so a consumer can't render their
  OWN graph. Subsumes the reachability of #26 (the nested-render panic is
  structurally unreachable until the render is parameterizable). Likely next
  (consumer-project / World) milestone.
- #29 (idea) — aura-engine doesn't re-export the scalar vocabulary (ScalarKind
  etc.) a graph-builder needs; one-crate ergonomics tidy.
- #16 (comment) — `aura graph` arg policy (help / unknown-arg) should unify with
  the still-open `aura run` strictness question.

The fieldtester read no implementation source; all artefacts are the consumer
crate + the two live render captures (render_clustered.txt / render_compiled.txt,
byte-identical across runs).
2026-06-05 22:38:39 +02:00

247 lines
16 KiB
Markdown
Raw 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 — milestone "Construction layer" — 2026-06-05
**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 "Construction layer" milestone (cycles 0012 composites + 0013 render)
promises that a downstream researcher can **build a graph fractally from a Rust
builder** — author leaf nodes and reusable *named composites* (a sub-graph
exposing one output), nest them arbitrarily, and `compile()` / `bootstrap()` the
param-generic `Blueprint` down to the flat, deterministic runnable instance
(C9/C19/C23). They can **introspect** the built graph as data and **render** it
as an ASCII DAG (`aura graph` clustered authored view, `aura graph --compiled`
flat inlined view) so the structure — and any mis-wiring — is visible
(C14/C22, #13). The composite boundary dissolves at inline (C23); a
composite-authored graph runs bit-identical to the hand-wired flat equivalent
(C1).
This fieldtest derives four end-to-end scenarios top-down from that promise, one
per carrier axis (ad), and drives them from the **public interface only**: the
design ledger (`docs/design/INDEX.md`), the crate doc-comments (rendered via
`cargo doc --no-deps`), the public re-export lists, and the `aura` CLI. **No
`crates/*/src` was read.** The construction-layer API was discovered entirely
from the ledger + rustdoc. The artefact under test was built from HEAD
(`cargo build --workspace`; the consumer crate compiled against the current
path-dep'd engine crates; the CLI driven via `cargo run -p aura-cli`, so HEAD is
always what ran).
## Examples
### fieldtests/milestone-construction-layer/mc_1_composite_build_run.rs — author + compile + run a named composite
- A standalone consumer crate (own `[workspace]`, path-deps on the engine
crates — the shape a C16 project takes) authors an SMA(2)/SMA(4)-cross as a
reusable named `Composite` (`sma_cross`, one price role fanning into both
SMAs, Sub's output as the exposed port), wires it into a harness `Blueprint`
(`source → composite → Exposure(0.5) → SimBroker(1e-4) → Recorder`),
`bootstrap()`s, and `run()`s it over a synthetic price ramp.
- Fits the milestone: this is the headline authoring story — build a graph
fractally from a Rust builder via a named composite, then compile/bootstrap/run
it.
- Outcome: built? **yes, first try.** ran? yes. matched expected? **yes** — 12
recorded equity rows, non-degenerate (`equity_pips` ramps 0 → 11.5 → 6.5),
the composite-authored harness produces a populated trace.
### fieldtests/milestone-construction-layer/mc_2_miswire_render.rs — a fast/slow swap is observable in graph-as-data + labels
- Builds the `sma_cross` composite twice — correct (fast→Sub.slot0,
slow→Sub.slot1) and swapped — and asserts the per-node `label()`s carry the
disambiguating params (`SMA(2)` / `SMA(4)`) and that the interior edge table
**differs** between the two. The property under test is the one the render
relies on: if a swap produced identical graph-as-data, no renderer could
surface it.
- Fits the milestone: the headline promise is "the render visibly surfaces the
mistake." This verifies the consumer-side substrate of that promise (labels +
edge table), complementing the `aura graph` CLI render exercised below.
- Outcome: built? yes. ran? yes. matched expected? **yes** — labels read
`["SMA(2)", "SMA(4)", "Sub"]`; correct edges `[{0→2.s0},{1→2.s1}]` vs swapped
`[{1→2.s0},{0→2.s1}]` — observably different.
### fieldtests/milestone-construction-layer/mc_3_nested_composite.rs — composite-inside-composite inlines, runs
- Nests the `sma_cross` composite inside an outer `strategy` composite (which
maps it through `Exposure(0.5)`), wires `strategy → SimBroker → Recorder`,
`bootstrap()`s and `run()`s.
- Fits the milestone: "nest composites arbitrarily" + "the compiled/flat view
handles nesting." This verifies the recursive-inline run-path from the
consumer side (the known sharp edge #26 is about the *clustered render*, not
the run path — see findings).
- Outcome: built? yes. ran? yes. matched expected? **yes** — the nested
composite recursively inlined to a flat runnable instance; 12 recorded rows.
### fieldtests/milestone-construction-layer/mc_4_introspect_graph.rs — walk the graph-as-data via read-only accessors
- Builds a `Blueprint` (composite + Exposure) and walks it purely through the
public accessors: `Blueprint::nodes()/sources()/edges()` and
`Composite::name()/nodes()/edges()/input_roles()/output()`, plus `label()` on
each boxed leaf, recursing into the composite interior.
- Fits the milestone: "introspect the built graph as data" (C9/C14) — can a
consumer walk it without engine internals?
- Outcome: built? yes (after one import fix — see finding). ran? yes. matched
expected? **yes** — the full interior (3 items, 1 role fanning to 2 targets,
Sub's output port, 2 interior edges) is reachable through the public accessors
alone.
### Render artefacts (CLI, built-in sample) — render_clustered.txt / render_compiled.txt
- `aura graph` and `aura graph --compiled` run live (via `cargo run -p
aura-cli`), captured as artefacts. Both render the engine's **built-in sample
blueprint** (the same SMA-cross harness). Byte-identical across two runs (C1
for the render).
## Findings
### [working] The full composite authoring → compile → bootstrap → run thread is authorable from the public interface
- Examples: mc_1, mc_3.
- What happened: the entire construction-layer authoring story —
`Composite::new(name, nodes, edges, input_roles, output)`,
`BlueprintNode::from(node)` / `BlueprintNode::Composite(..)`,
`Blueprint::new(..).bootstrap()`, `Harness::run` — compiled on the **first
attempt** from rustdoc signatures + the ledger (C9/C19/C23) alone, for both a
single composite and a composite-in-composite. Both produced populated,
non-degenerate recorded traces.
- Why working: the milestone's central promise — build a graph fractally from a
Rust builder and compile/bootstrap/run it — is delivered, and the public
surface (ledger + rustdoc) is self-sufficient for a downstream author. The
recursive inline of a nested composite works end to end on the run path.
- Recommended action: carry-on.
### [working] The graph-as-data accessors let a consumer walk the built graph (incl. composite interiors) with no engine internals
- Example: mc_4.
- What happened: `Blueprint::{nodes,sources,edges}` and
`Composite::{name,nodes,edges,input_roles,output}` together expose the whole
graph as read-only data; a recursive walk reaches every leaf label, every
role fan-out, the output port, and every interior edge. `node.label()`
dispatches off the boxed leaf without importing the `Node` trait.
- Why working: C9 "the built graph is introspectable runtime data" and the #13
graph-as-data promise hold from the consumer side.
- Recommended action: carry-on.
### [working] Per-node labels disambiguate identical node types, and a mis-wire changes the graph-as-data
- Examples: mc_2, render_clustered.txt.
- What happened: `label()` overrides carry params (`SMA(2)` vs `SMA(4)`), so the
two SMAs are distinguishable; a fast/slow swap changes the interior edge
table. The `aura graph` clustered render shows both SMAs feeding `Sub` with
the labels present, so a reader can trace which leg goes where. Render is
byte-identical across runs (C1).
- Why working: the C8-refinement label mechanism does exactly what the milestone
needs — it makes a swap *readable*. This is the substrate of the headline
"render surfaces a mis-wiring" promise.
- Recommended action: carry-on.
### [friction] `aura graph` renders ONLY the built-in sample — a consumer cannot render the graph they authored
- Examples: mc_1/mc_2/mc_3 (authored blueprints) vs the CLI render artefacts.
- What happened: `aura graph` and `aura graph --compiled` are hard-wired to one
built-in sample blueprint. Any extra argument is silently ignored:
`aura graph --bogus`, `aura graph mc_1_composite_build_run.rs`, and even
`aura graph --help` all render the **same** built-in sample and exit 0. There
is no path (flag, file, stdin) to point the renderer at a consumer's own
`Blueprint`.
- Why friction: the milestone's headline payoff — "render the structure so any
**mis-wiring** is visible before/around a run" — is, at the CLI level, only
available for the engine's own sample. A downstream researcher who authored
`mc_2`'s *swapped* cross (the literal mis-wire the feature exists to catch)
cannot feed it to `aura graph`; they fall back to introspecting the graph-as-data
by hand (mc_2/mc_4) or writing their own ascii-dag adapter (which lives only in
`aura-cli`, not a reusable surface). The render-as-a-library affordance that
would let a consumer render their own graph is absent. Task completed (the
render exists and is correct), but the consumer is forced into a hand-rolled
workaround to apply it to their own work.
- Recommended action: plan — expose a render entry point a consumer can call on
their own `Blueprint` (a library `render(&Blueprint) -> String` in a face
crate, or `aura graph` accepting a project/blueprint selector). Pairs naturally
with the deferred `aura new` / experiment-builder surface.
### [friction] The construction surface (aura-engine) does not re-export the core scalar vocabulary a graph-builder needs
- Example: mc_4 (initial `use aura_engine::{… ScalarKind …}` failed to compile:
"no `ScalarKind` in the root … consider importing `aura_core::ScalarKind`").
- What happened: building a `Blueprint`/`SourceSpec`/`Recorder` requires
`ScalarKind` (and, for sources/recorders, `Scalar` / `Firing` / `Timestamp`),
but `aura-engine` — the crate that owns `Blueprint`, `Composite`, `Edge`,
`Target`, `SourceSpec` — re-exports none of them. `SourceSpec { kind:
ScalarKind, .. }` is an `aura-engine` struct whose own field type must be
imported from a *different* crate (`aura-core`). A downstream author must know
to reach across crate boundaries for the vocabulary the engine's own public
types demand.
- Why friction: it is a paper cut, not a blocker — the compiler error names the
right import — but it makes the "build a graph from a Rust builder" surface
more than one `use aura_engine::*`. The construction API and the scalar
vocabulary it consumes live in two crates with no convenience re-export.
- Recommended action: plan (tidy) — re-export the core scalar vocabulary
(`Scalar`, `ScalarKind`, `Firing`, `Timestamp`) from `aura-engine` (or a
prelude) so a graph-builder has a single import surface. Low priority.
### [spec_gap] `aura graph --help` / unknown args → render the sample, exit 0 (no help, args silently swallowed)
- Examples: render probes — `aura graph --help` and `aura graph --bogus` both
print the clustered sample and exit 0.
- What happened: the `graph` subcommand keys only on the presence of the exact
`--compiled` token; every other argument (including `--help`) falls through to
the clustered view. No usage/help text, no rejection of unknown flags.
- Why spec_gap: the milestone/CLI contract does not pin how `aura graph` handles
unknown or `--help` arguments; the binary chose the lenient "ignore and render
the default view" reading. An equally plausible reading rejects unknown flags
(exit 2) and prints help on `--help`. This re-confirms, on the new `graph`
subcommand, the same lenient-arg gap the walking-skeleton fieldtest recorded
for `aura run <junk>` and `aura --help`.
- Recommended action: ratify-or-tighten — decide a single CLI arg policy
(reject unknown flags + `--help`→stdout/exit 0) across **both** `run` and
`graph`, rather than per-subcommand. Low priority; carried/expanded from the
walking-skeleton finding.
### [spec_gap] The known nested-composite render panic (#26) is unreachable from the public CLI today — its consumer impact is latent, not live
- Example: mc_3 (authored a nested composite; could not feed it to `aura graph`).
- What happened: the carrier flags #26 — the clustered blueprint view has an
`unimplemented!` on nested composites — as a sharp edge to classify. As a
downstream consumer I **could not reach it**: `aura graph` renders only the
built-in single-level sample (see the friction above), so there is no public
surface that feeds a *nested* blueprint into the clustered renderer. The panic
is real in the code but **structurally unreachable** from the shipped CLI; the
run-path nesting (mc_3) works fine.
- Why spec_gap: the milestone promise says the render makes nesting visible, but
the only render entry point is the fixed sample, so "render a nested composite"
is not actually exercisable by a consumer — the gap and the latent panic are
two faces of the missing "render my own graph" affordance. Severity of #26 as a
*consumer-facing* bug is currently **low/latent** (no public trigger), but it
becomes **live** the moment the render is made parameterizable (the friction
above) — at which point a consumer rendering a nested clustered view would
panic.
- Recommended action: tighten the design ledger / sequence #26 — record that the
"render my own graph" affordance (the friction above) and the #26 clustered
nesting fix are **coupled**: shipping the former without the latter exposes the
panic to consumers. The audit's own note ("resolve #26 before the next
render-touching cycle") already points this way; this fieldtest confirms the
coupling empirically. Classify #26 as the blocker *for the parameterizable-render
cycle*, not for this milestone (which ships only the sample render).
## Recommendation summary
| Finding | Class | Action |
|---|---|---|
| Composite authoring → compile → bootstrap → run thread authorable, populated | working | carry-on |
| Graph-as-data accessors walk the built graph (incl. interiors) | working | carry-on |
| Labels disambiguate; a swap changes graph-as-data; render deterministic | working | carry-on |
| `aura graph` renders only the built-in sample (no render-my-own-graph) | friction | plan |
| aura-engine doesn't re-export the core scalar vocabulary | friction | plan (tidy) |
| `aura graph --help` / unknown args → render sample, exit 0 | spec_gap | ratify-or-tighten (unify CLI arg policy) |
| #26 nested-render panic unreachable from CLI today (latent, coupled to render-my-own-graph) | spec_gap | tighten ledger / sequence #26 with the parameterizable-render cycle |
## Milestone roll-up
**The "Construction layer" milestone DELIVERS its core promise.** A downstream
researcher can author leaf nodes and reusable named composites, nest them
arbitrarily, and `compile()`/`bootstrap()`/`run()` the param-generic `Blueprint`
down to a flat, deterministic, populated-trace instance (C9/C19/C23) — all from
the public interface, first try, including a composite-in-composite. The
graph-as-data accessors let a consumer walk the built graph without engine
internals, and the C8-refinement labels make a mis-wire readable in both the
graph-as-data and the `aura graph` render.
The gap is in the **render's reach**, not its correctness: `aura graph` is
hard-wired to one built-in sample, so the headline "render any mis-wiring before
a run" payoff is, at the CLI level, only demonstrated on the engine's own
example — a consumer cannot point it at the graph they authored, which is exactly
where a mis-wire would live. That single missing affordance ("render my own
graph") subsumes both the friction and the latent reachability of the #26
nested-render panic: making the render parameterizable is the next step, and #26
must land with it. **No bugs found** (the #26 panic is real in code but
structurally unreachable from the public surface today). Three `working`, two
`friction`, two `spec_gap` — none blocking the milestone gate.