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

16 KiB
Raw Blame History

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.