docs,engine: drop coined "compilat", use FlatGraph / "flat graph"

"Compilat" (German "Kompilat") was a coined noun for the product of the
bootstrap compilation — neither English nor a natural fit. The runtime
artifact already has a code identifier for exactly this thing: the
`FlatGraph` struct (harness.rs). Replace the coinage with that identifier:

- prose mentions          -> "flat graph" (mirrors the type, reads plainly)
- definitional anchors    -> `FlatGraph` (C11, C23, the running-graph line)
- `render_compilat`       -> `render_flat_graph` (historical render symbol;
                             keeps the `render_blueprint` / `render_flat_graph`
                             source-vs-product pairing)
- `intra-compilat`        -> `intra-graph`
- `from_compilat` (test)  -> `from_flat`

"compilation" / "re-compilation" / "bootstrap-as-compilation" (the process,
ordinary English) are deliberately left untouched. Behaviour-preserving:
only comments, design ledger, specs/plans, one test-local variable and its
assert messages change. Full workspace test suite green; clippy clean.
This commit is contained in:
2026-06-14 17:02:15 +02:00
parent 8b330e3eaa
commit 21c1621bd0
41 changed files with 186 additions and 186 deletions
+2 -2
View File
@@ -114,10 +114,10 @@ design decision, not a refactor, and belongs in the ledger.
blueprint). The harness — sources + strategy + broker node(s) + sinks — is
the root sim graph, itself bootstrapped, and is C1's disjoint unit; its
structural axes form the experiment matrix, its tuning params the sweep.
**The bootstrap is a compilation** to a flat, type-erased *compilat*, wired
**The bootstrap is a compilation** to a flat, type-erased `FlatGraph`, wired
by raw index (composites inline — a composite is an authoring-level node, not
a runtime object; names survive only as non-load-bearing debug symbols, as
today). The compilat is the target of **behaviour-preserving** optimisation,
today). The flat graph is the target of **behaviour-preserving** optimisation,
C1 the correctness invariant — intra-graph (CSE/DCE) and across a sweep family
(sweep-invariant sub-graph computed once, shared via C11/C12). See C9/C19/C23.
12. **The World is the product; the playground is a trace explorer.** Three
+15 -15
View File
@@ -1,6 +1,6 @@
//! The construction layer (C9/C19/C23): a named, param-generic graph-as-data
//! ([`Composite`]) that **compiles** to the flat, type-erased instance the run loop
//! already runs (the *compilat*, a [`crate::FlatGraph`]). The unit of reuse is the
//! already runs (a [`crate::FlatGraph`]). The unit of reuse is the
//! [`Composite`]: a nestable sub-graph fragment exposing an output record (one port,
//! K re-exported fields; C8) and input roles (each open, or — at the root —
//! source-bound). `compile` **inlines** the nesting into the flat `FlatGraph` the
@@ -8,7 +8,7 @@
//! blueprint (there is no separate `Blueprint` type — a fully source-bound composite
//! is the runnable root).
//!
//! The compilat is wired by raw index, **not by name** (C23): a composite's
//! The flat graph is wired by raw index, **not by name** (C23): a composite's
//! boundary dissolves at compile time; field/role names, where kept, are
//! non-load-bearing debug symbols (as `FieldSpec.name` already is). This module
//! adds no optimisation pass (CSE/DCE, sweep-invariant hoisting are deferred,
@@ -25,7 +25,7 @@ use crate::{GridSpace, RunReport, SweepFamily};
/// One re-exported field of a composite's output record: an interior
/// `(node, output-field)` surfaced at the boundary under `name`. `name` is a
/// non-load-bearing render/debug symbol (C23) — like `FieldSpec.name` and
/// `Composite.name`, it does not reach the compilat.
/// `Composite.name`, it does not reach the flat graph.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OutField {
pub node: usize,
@@ -157,7 +157,7 @@ impl Composite {
/// Build a composite from its authored name, interior items, interior edges
/// (local indices), input roles, and output record. The `name` is a
/// non-load-bearing render symbol (the cluster title for #13); it does not
/// reach the compilat (the boundary dissolves at inline, C23).
/// reach the flat graph (the boundary dissolves at inline, C23).
pub fn new(
name: impl Into<String>,
nodes: Vec<BlueprintNode>,
@@ -473,7 +473,7 @@ fn resolve(space: &[ParamSpec], bound: &[(String, Scalar)]) -> Result<Vec<Scalar
Ok(point)
}
/// A construction-phase fault, caught before the flat compilat reaches
/// A construction-phase fault, caught before the flat graph reaches
/// `Harness::bootstrap`.
#[derive(Debug, PartialEq, Eq)]
pub enum CompileError {
@@ -483,7 +483,7 @@ pub enum CompileError {
RoleKindMismatch { role: usize },
/// The output port names a missing interior node or output field.
OutputPortOutOfRange,
/// The lowered flat compilat failed `Harness::bootstrap`'s checks (kind
/// The lowered flat graph failed `Harness::bootstrap`'s checks (kind
/// mismatch, bad index, or directed cycle).
Bootstrap(BootstrapError),
/// An injected param value's scalar kind does not match the slot's declared
@@ -631,7 +631,7 @@ fn collect_params(items: &[BlueprintNode], prefix: &str, out: &mut Vec<ParamSpec
}
}
/// How one blueprint item resolved into the flat compilat. Edges and source
/// How one blueprint item resolved into the flat graph. Edges and source
/// targets to/from an item are resolved through this.
enum ItemLowering {
/// A leaf lowered to exactly one flat node at this index.
@@ -701,10 +701,10 @@ fn inline_composite(
flat_edges: &mut Vec<Edge>,
) -> Result<ItemLowering, CompileError> {
// `name` is the non-load-bearing render symbol (#13); it dissolves at inline
// (C23 — the boundary does not reach the compilat), so it is not destructured.
// (C23 — the boundary does not reach the flat graph), so it is not destructured.
// Node names join the same non-load-bearing debug-symbol class: they qualify the
// param-space path at construction but are dropped at lowering — the injected
// scalar `params: &[Scalar]` arg drives `lower_items` below; the compilat stays
// scalar `params: &[Scalar]` arg drives `lower_items` below; the flat graph stays
// wired by raw index.
let Composite { name: _, nodes, edges, input_roles, output } = c;
let item_count = nodes.len();
@@ -1901,16 +1901,16 @@ mod tests {
// the same blueprint, actually compiled to its flat node array; each flat
// node's own declared params, concatenated in flat-node order
let flat = bp.compile_with_params(&[Scalar::I64(2), Scalar::I64(4), Scalar::F64(0.5)]).expect("harness compiles");
let from_compilat: Vec<ParamSpec> =
let from_flat: Vec<ParamSpec> =
flat.signatures.iter().flat_map(|s| s.params.clone()).collect();
// same count, same per-slot kind, same order — the projection mirrors the
// compilation (names differ: param_space path-qualifies, the raw node does
// not, so compare on the load-bearing axis, kind-by-slot)
assert_eq!(space.len(), from_compilat.len(), "param count must match the compilat");
assert_eq!(space.len(), from_flat.len(), "param count must match the flat graph");
assert_eq!(
space.iter().map(|p| p.kind).collect::<Vec<_>>(),
from_compilat.iter().map(|p| p.kind).collect::<Vec<_>>(),
from_flat.iter().map(|p| p.kind).collect::<Vec<_>>(),
"per-slot param kinds must line up with the compiled flat-node order",
);
// the realistic harness's concrete space: two SMA lengths (I64) + Exposure
@@ -2060,16 +2060,16 @@ mod tests {
// the same blueprint, compiled to its flat node array; each flat node's
// own declared params, concatenated in flat-node order
let flat = bp.compile_with_params(&[Scalar::I64(2), Scalar::I64(4), Scalar::F64(1.0), Scalar::F64(-1.0)]).expect("nested composite compiles");
let from_compilat: Vec<ParamSpec> =
let from_flat: Vec<ParamSpec> =
flat.signatures.iter().flat_map(|s| s.params.clone()).collect();
// same count, same per-slot kind, same order — the nested projection
// mirrors the compilation (names differ: param_space path-qualifies, the
// raw node does not, so compare on the load-bearing axis, kind-by-slot)
assert_eq!(space.len(), from_compilat.len(), "param count must match the compilat");
assert_eq!(space.len(), from_flat.len(), "param count must match the flat graph");
assert_eq!(
space.iter().map(|p| p.kind).collect::<Vec<_>>(),
from_compilat.iter().map(|p| p.kind).collect::<Vec<_>>(),
from_flat.iter().map(|p| p.kind).collect::<Vec<_>>(),
"per-slot param kinds must line up with the compiled flat-node order, under nesting",
);
// pin the concrete shape: two Sma lengths (I64), Sub none, two LinComb
+1 -1
View File
@@ -2,7 +2,7 @@
//!
//! A fluent, additive authoring surface that wires a blueprint by typed node
//! handles and port/field *names*, resolving them to the raw-index `Composite`
//! at a single fallible `build()`. The compilat stays index-wired (C23): names
//! at a single fallible `build()`. The flat graph stays index-wired (C23): names
//! are resolved at the authoring boundary and never reach `FlatGraph` — the same
//! posture param-name resolution already has (`Binder`, blueprint.rs). Resolution
//! mirrors `PrimitiveBuilder::bind`'s collect-then-reject (node.rs), but returns
+1 -1
View File
@@ -52,7 +52,7 @@ pub struct SourceSpec {
}
/// The flat, type-erased, index-wired output of `Composite::compile` — the target
/// `Harness::bootstrap` consumes (C23's "compilat", now a named type). `signatures[i]`
/// `Harness::bootstrap` consumes (C23's "flat graph", now a named type). `signatures[i]`
/// is the static signature of `nodes[i]` (gathered from each primitive's builder at
/// lowering); `bootstrap` reads kinds/output from it and `nodes[i].lookbacks()` for
/// buffer depth. `sources` are the lowered bound roles, in role-declaration order.
+27 -27
View File
@@ -12,7 +12,7 @@ C10 was later reframed in cycle 0007 — the intent/exposure stream is the prima
output, the position table a derived layer — see C10). C19C22 were added as the
construction / World / playground layer; C23 and the C9/C19 compilation
refinements were settled 2026-06-05 for the **Construction-layer** milestone (the
blueprint→compilat reading of composites and graph optimisation).
blueprint→flat-graph reading of composites and graph optimisation).
The `CLAUDE.md` **Domain invariants** section is the compressed, always-loaded
summary of the
subset that agents must never violate; this file is the fuller form with
@@ -265,11 +265,11 @@ sweep covers is an experiment axis, #32/C20; the node declares the knob's existe
and type, never its search interval). (2) Identity is **positional** (the slot,
C23 "by index, not name"); the path-qualified `name` is a non-load-bearing debug
symbol (like `FieldSpec.name`) — uniqueness is at the slot. **Refinement (cycle
0032):** identity in the **compilat** stays positional (C23, unchanged), but the
0032):** identity in the **flat graph** stays positional (C23, unchanged), but the
`param_space()` **name projection** — the authoring / by-name address space (the
surface a sweep axis or single-run binding addresses) — must be **injective** for a
blueprint to compile (a duplicated path is unaddressable; see C9). Two layers:
positional wiring below (the compilat), an injective name address space above (the
positional wiring below (the flat graph), an injective name address space above (the
authoring boundary). So same-type siblings in one composite no longer silently
share a name — they must be `.named(...)` apart, which is also what disambiguates a
same-type fan-in (C9). A vector knob (`LinComb.weights`) expands to `N` flat
@@ -332,7 +332,7 @@ never consumers, so only interior input slots are subject to the rule. There is
optional-input concept**: every declared input port is required (no shipped node runs
meaningfully without one; an unwarmed mode-A input is *wired-but-not-yet-valued*, not
unwired). The check is **index-based and name-free** — it touches no name machinery
and emits nothing into the compilat, so C23 is untouched (it proves the existing
and emits nothing into the flat graph, so C23 is untouched (it proves the existing
raw-index wiring is total and single-valued). Inherited identically by the raw
`Composite::new` path and the `GraphBuilder::build()` path (both compile via
`compile_with_params`).
@@ -358,7 +358,7 @@ sub-graph into the one flat instance (C19/C23): the composite *boundary* dissolv
into the raw index wiring the run loop already consumes, and names stay
**non-load-bearing** (informative debug symbols only, as `FieldSpec.name` already
is). So "nestable arbitrarily" and "graph-as-data" hold at the **blueprint
(source)** level; the running graph is the flat, index-wired **compilat**. The earlier reading — *a composite survives as a `Box<dyn Node>`
(source)** level; the running graph is the flat, index-wired **`FlatGraph`**. The earlier reading — *a composite survives as a `Box<dyn Node>`
driving a nested sub-engine* — is **explicitly rejected**: it would keep the
interior opaque to the cross-graph optimiser (C23) and add a runtime sub-loop the
flat model does not need. Inlining is what makes the composite boundary free.
@@ -376,7 +376,7 @@ completion, not a contract change. A strategy composite is simply the K=1 case
they live at the blueprint boundary and in render (cycle 0022/#46 folds each onto
its producing node as a `name := …` binding; originally `[out:<name>]` markers, #13)
but are dropped at lowering — `ItemLowering::Composite.output` is `Vec<(usize,
usize)>`, raw index pairs only, so the compilat is name-free (verified: the
usize)>`, raw index pairs only, so the flat graph is name-free (verified: the
compiled-view render stayed bit-identical across this change).
**Realization (cycle 0019 — name the composite boundary, #41; param-overlay retired,
cycle 0031).** The named-projection shape covers the surviving boundary edge-kinds:
@@ -390,9 +390,9 @@ carries a name (default = its lowercased type label, override via `.named()`), a
same-type fan-in is distinguished by naming the colliding legs, the same single act
that qualifies their param paths. Like the output and role names, **node names are
non-load-bearing** (C23): they live at the blueprint boundary and in render but are
dropped at lowering — the compilat is wired by raw index. The full composite
dropped at lowering — the flat graph is wired by raw index. The full composite
boundary signature (named inputs, multi-outputs) and the per-node param path are
legible without changing the compilat.
legible without changing the flat graph.
**Refinement (param-namespace injectivity, cycle 0032; supersedes the fan-in
distinguishability check).** A blueprint compiles only if its `param_space()` name
projection is **injective** — every path-qualified knob name is unique. A duplicated
@@ -416,7 +416,7 @@ That breadth guarded **render identity**, dead since the renderer was retired in
0026; both extra rejections are **intentionally dropped**. A future node-/wiring-name
distinguishability check, if ever wanted (e.g. for the WASM graph view's #21
thread), is decoupled from param-space injectivity. Construction-phase only; the
compilat stays name-free (C23).
flat graph stays name-free (C23).
### C10 — Strategy output is an intent/exposure stream; position management is a decoupled derived layer; brokers are downstream nodes
**Guarantee.** A strategy's primary, backtestable output is **not** an equity
@@ -670,13 +670,13 @@ of rewriting code. Naming the build phase makes the implicit "wiring" of C7/C12
explicit.
**Refinement (Construction-layer milestone, 2026-06-05).** This binding is a
**compilation**: the param-generic, named blueprint (source) is lowered to a flat,
type-erased **compilat** (C7) **wired by raw index, not by name**
type-erased **flat graph** (C7) **wired by raw index, not by name**
(`Edge { from, to, slot, from_field }`): composite boundaries dissolve entirely,
and field / role names are demoted to **non-load-bearing** debug symbols (as
`FieldSpec.name` already is). "No recompile" above means no **Rust /
cdylib** rebuild (C12/C13: the cdylib loads once); re-deriving an instance per
param-set is a cheap **graph re-compilation**, not a code recompile. Naming the
build phase a compilation makes its successor explicit: the compilat is the target
build phase a compilation makes its successor explicit: the flat graph is the target
of behaviour-preserving optimisation (C23).
**Realization (cycle 0016 — param-set injection).** The bootstrap now binds an
injected param-set, realizing C12's "params injected at graph build (the optimizer
@@ -697,7 +697,7 @@ traversal hazard) and the value reaches the node at the slot the sweep enumerate
Arity is checked up front (`param_space().len()`); a wrong-kind or wrong-length
vector is a typed `CompileError::{ParamKindMismatch, ParamArity}` (the typed-value
check C8 deferred). The lowering/edge/source rewrite is structurally unchanged, so
the **compilat stays bit-identical** for a given point (C23, the correctness
the **flat graph stays bit-identical** for a given point (C23, the correctness
invariant). The value *domain* (e.g. `length ≥ 1`) stays the constructor's own
`assert`; the search-range is still the run's (#32/C20). One value-empty leaf
detail for C22: the blueprint view (pre-run, param-generic) labels a leaf by **bare
@@ -726,7 +726,7 @@ definitions pass recurses). The interactive *enter/focus* counterpart (a composi
collapsed to a navigable node) is the playground's, parked as a separate concern
(#37); the static CLI keeps the all-at-once definitions form (#38).
**Realization (cycle 0024 — the root is the fully-bound composite; the compilat is
**Realization (cycle 0024 — the root is the fully-bound composite; the flat graph is
a named type).** `struct Blueprint` is **deleted**: the root graph IS a `Composite`,
and `compile_with_params`/`bootstrap_with_params`/`param_space` are its methods.
What distinguished the root — its bound data sources — is now a property of its input
@@ -739,7 +739,7 @@ source-bound, governed by the same conditions as any other node. Compilation now
targets a **named type**: `compile` validates structurally **pre-build** (via
`signature()`, no node constructed — an ill-typed wiring is caught before any build
closure fires) and emits `FlatGraph { nodes, signatures, sources, edges }` — the
C23 compilat, now first-class — which `Harness::bootstrap` consumes (kinds/firing
C23 flat graph, now first-class — which `Harness::bootstrap` consumes (kinds/firing
from the carried signatures, buffer depth from `lookbacks()`). The per-flat-node
signature travels beside the node, so `bootstrap` reads it without a built-node
`schema()` call (which no longer exists, see the C8 0024 realization).
@@ -785,7 +785,7 @@ shrink propagates for free, and chained binds reconstruct the correct positional
vector because each layer computes its slot index relative to the param list it
sees. C23 is unaffected: `bind` resolves the param **name** to a position at
**authoring** time (the by-name authoring address space, the 0032 amendment) and
the compilat stays wired by raw index — the name never reaches it. The
the flat graph stays wired by raw index — the name never reaches it. The
complementary question — exporting a **named frozen** strategy (all/most knobs
bound) as a reusable blueprint *value* — is deferred (#60); `bind` ships only the
per-knob overlay, no registry (C9/C10 intact).
@@ -884,23 +884,23 @@ Recorded streams are sparse and timestamped (a record per fired cycle, tagged
### C23 — Graph compilation and behaviour-preserving optimisation
**Guarantee.** The bootstrap (C19) is a **compilation**: it lowers a param-generic,
named **blueprint** (the authoring source — nodes, composites, strategy, harness;
C8/C9/C20) into a flat, type-erased **compilat** — the frozen runnable
C8/C9/C20) into a flat, type-erased **`FlatGraph`** — the frozen runnable
instance (C7) — **wired by raw index, not by name** (`Edge { from, to, slot,
from_field }`). Composites are **inlined** at this step (C9): the composite
*boundary* dissolves entirely (there is no composite in the compilat). The
*boundary* dissolves entirely (there is no composite in the flat graph). The
blueprint's field / input-role *names* are **non-load-bearing** — the wiring
resolves by index, and names survive at most as **informative debug symbols**
(exactly as `FieldSpec.name` already is, C8), kept for tracing / rendering (C9
graph-as-data, #13) but carrying no run semantics. The compilat is then the target of **behaviour-preserving
graph-as-data, #13) but carrying no run semantics. The flat graph is then the target of **behaviour-preserving
optimisation** — any transform that leaves every observable sink trace
bit-identical (C1 is the correctness invariant) — on two levels:
- **intra-compilat** (within one graph): **common-subexpression elimination**
- **intra-graph** (within one graph): **common-subexpression elimination**
two identical nodes (same type, same bound params, same input source) merge to
one with output fan-out, so fractal composition (C9) costs no redundant compute —
and **dead-node elimination** — a node on no path to a sink is dropped.
Pure-consumer sinks (C8, no output) are never CSE candidates, so their
out-of-graph side effects are preserved by construction.
- **across the sweep family** (over the family of compilats one blueprint yields
- **across the sweep family** (over the family of flat graphs one blueprint yields
under a param sweep, C12): the sub-graph whose nodes carry **no swept param** and
whose inputs are **all sweep-invariant** (transitively from the sources — same
data window) is **loop-invariant** w.r.t. the sweep and is computed **once**; its
@@ -915,16 +915,16 @@ analysed or rewritten across its boundary, so it forecloses both levels.
for these rewrites — a behaviour-preserving transform is only meaningful because
"same input → bit-identical run" makes "same result" decidable, and a
sweep-invariant sub-graph is reproducible enough to compute once and share. The
compilat representation (C19) is the necessary condition: only a flat, inspectable
flat graph representation (C19) is the necessary condition: only a flat, inspectable
graph-as-data — with each node's declared param-ranges (C8) — lets the compiler
identify identical sub-expressions, dead nodes, and the sweep-invariant frontier.
This is precisely why a composite is an inlining (C9) and not a runtime
sub-engine: the flat compilat is what makes the whole graph optimisable.
**Status (Construction-layer milestone).** The compilat *representation*
sub-engine: the flat graph is what makes the whole graph optimisable.
**Status (Construction-layer milestone).** The flat graph *representation*
blueprint → inline / compile → flat instance — is the load-bearing substrate built
**first**; the optimisation passes (intra-compilat CSE/DCE, sweep-invariant
**first**; the optimisation passes (intra-graph CSE/DCE, sweep-invariant
hoisting) are **deferred**, behaviour-preserving follow-on work, each gated by a
"compilat with pass ≡ compilat without pass, bit-identical run" test (C1). The
"flat graph with pass ≡ flat graph without pass, bit-identical run" test (C1). The
sweep-level pass further presupposes per-node param declarations (C8 — landed in
cycle 0015 as `name`+`kind`; the swept *range* still pending #32/C20) and the sweep
orchestration itself (C12/C21).
@@ -934,9 +934,9 @@ The bootstrap-as-compilation now includes one structural gate:
C12-C19), run before name resolution. It reads the **boundary** name projection — the
authoring address space — and rejects a duplicated path (`DuplicateParamPath`). Node
names stay **non-load-bearing**: they qualify the param path at construction and are
dropped at lowering (the compilat is wired by raw index, unchanged). The check makes
dropped at lowering (the flat graph is wired by raw index, unchanged). The check makes
the by-name *authoring address space* injective; it does **not** make names
load-bearing in the compilat.
load-bearing in the flat graph.
---
+11 -11
View File
@@ -1,4 +1,4 @@
# Blueprint → compilat: composite inlining — Implementation Plan
# Blueprint → flat graph: composite inlining — Implementation Plan
> **Parent spec:** `docs/specs/0012-blueprint-compile-composites.md`
>
@@ -13,10 +13,10 @@ prove an SMA-cross composite runs bit-identically to today's hand-wired graph (C
**Architecture:** A new module `crates/aura-engine/src/blueprint.rs` sits *above*
`Harness::bootstrap`. `Blueprint::compile()` recursively inlines every `Composite`
(append interior nodes at an offset, rewrite interior edges, fan input roles out,
resolve the one output port), producing the same flat compilat a hand-wiring would.
resolve the one output port), producing the same flat graph a hand-wiring would.
The run loop, `bootstrap`'s signature, and `Edge`/`Target`/`SourceSpec`/`Node` are
untouched; bootstrap's existing kind- and Kahn-cycle-check validate the lowered
compilat. Optimisation passes (C23) are explicitly out of scope.
flat graph. Optimisation passes (C23) are explicitly out of scope.
**Tech Stack:** Rust, `aura-engine` (depends on `aura-core`; `aura-std` is a
dev-dependency reachable from tests). No new external dependencies (C16).
@@ -57,12 +57,12 @@ Create `crates/aura-engine/src/blueprint.rs` with exactly this content:
```rust
//! The construction layer (C9/C19/C23): a named, param-generic graph-as-data
//! (`Blueprint`) that **compiles** to the flat, type-erased instance the run loop
//! already runs (the *compilat*). The unit of reuse is the [`Composite`]: a
//! already runs (the *flat graph*). The unit of reuse is the [`Composite`]: a
//! nestable sub-graph fragment exposing one output port (C8) and named input
//! roles, which `compile` **inlines** into the flat `(nodes, sources, edges)` the
//! unchanged [`crate::Harness::bootstrap`] consumes.
//!
//! The compilat is wired by raw index, **not by name** (C23): a composite's
//! The flat graph is wired by raw index, **not by name** (C23): a composite's
//! boundary dissolves at compile time; field/role names, where kept, are
//! non-load-bearing debug symbols (as `FieldSpec.name` already is). This module
//! adds no optimisation pass (CSE/DCE, sweep-invariant hoisting are deferred,
@@ -479,7 +479,7 @@ In `crates/aura-engine/src/blueprint.rs`, add the `CompileError` enum immediatel
after the `Composite` impl block (before `pub struct Blueprint`):
```rust
/// A construction-phase fault, caught before the flat compilat reaches
/// A construction-phase fault, caught before the flat graph reaches
/// `Harness::bootstrap`.
#[derive(Debug, PartialEq, Eq)]
pub enum CompileError {
@@ -489,7 +489,7 @@ pub enum CompileError {
RoleKindMismatch { role: usize },
/// The output port names a missing interior node or output field.
OutputPortOutOfRange,
/// The lowered flat compilat failed `Harness::bootstrap`'s checks (kind
/// The lowered flat graph failed `Harness::bootstrap`'s checks (kind
/// mismatch, bad index, or directed cycle).
Bootstrap(BootstrapError),
}
@@ -499,9 +499,9 @@ Then add the `compile` and `bootstrap` methods inside `impl Blueprint` (after
`new`):
```rust
/// Lower to the flat compilat: inline every composite (recursive), offset
/// Lower to the flat graph: inline every composite (recursive), offset
/// interior indices, rewrite edges, and fan input roles out. The run loop and
/// `bootstrap`'s data model are unchanged; the lowered compilat is wired by raw
/// `bootstrap`'s data model are unchanged; the lowered flat graph is wired by raw
/// index (C23).
// The flat triple is exactly `Harness::bootstrap`'s argument list; naming it
// would be a speculative type alias this cycle (same call as the CLI's sample).
@@ -533,7 +533,7 @@ Then add the `compile` and `bootstrap` methods inside `impl Blueprint` (after
Ok((flat_nodes, flat_sources, flat_edges))
}
/// Compile, then hand the flat compilat to the unchanged `Harness::bootstrap`.
/// Compile, then hand the flat graph to the unchanged `Harness::bootstrap`.
pub fn bootstrap(self) -> Result<Harness, CompileError> {
let (nodes, sources, edges) = self.compile()?;
Harness::bootstrap(nodes, sources, edges).map_err(CompileError::Bootstrap)
@@ -544,7 +544,7 @@ Finally add the free lowering helpers and the `ItemLowering` enum at the end of
file (after the `impl Blueprint` block, before `#[cfg(test)] mod tests`):
```rust
/// How one blueprint item resolved into the flat compilat. Edges and source
/// How one blueprint item resolved into the flat graph. Edges and source
/// targets to/from an item are resolved through this.
enum ItemLowering {
/// A leaf lowered to exactly one flat node at this index.
+8 -8
View File
@@ -29,7 +29,7 @@ v0.9.1, new `graph.rs`), `docs/design/INDEX.md` (C8 refinement note).
- Modify: `crates/aura-std/src/sma.rs:22-46`, `sub.rs:27-47`, `exposure.rs:23-39`, `sim_broker.rs:59-85`, `add.rs:36-56`, `lincomb.rs:42-66`, `recorder.rs:31-58``label()` overrides.
- Test: `crates/aura-std/src/sma.rs:48` (tests module) — disambiguation test.
- Modify: `crates/aura-engine/src/blueprint.rs``Composite.name` field (54-59), widened `Composite::new` (64-71), accessors on `Composite` (61-90) and `Blueprint` (115-161), and the 7 `Composite::new` call sites (356, 433, 481, 513, 527, 541, 631).
- Create: `crates/aura-cli/src/graph.rs` — the ascii-dag adapter (`render_blueprint`, `render_compilat`).
- Create: `crates/aura-cli/src/graph.rs` — the ascii-dag adapter (`render_blueprint`, `render_flat_graph`).
- Modify: `crates/aura-cli/Cargo.toml:12-15` — add `ascii-dag = "0.9.1"`.
- Modify: `crates/aura-cli/src/main.rs` — imports (9-14), `mod graph;`, sample builders, `graph` dispatch arm + usage strings (114-126). `sample_harness` (42-78) and `run_sample` (83-112) are NOT touched.
- Test: `crates/aura-cli/src/main.rs:128-168` (tests module) — render tests.
@@ -274,7 +274,7 @@ to:
/// Build a composite from its authored name, interior items, interior edges
/// (local indices), input roles, and output port. The `name` is a
/// non-load-bearing render symbol (the cluster title for #13); it does not
/// reach the compilat (the boundary dissolves at inline, C23).
/// reach the flat graph (the boundary dissolves at inline, C23).
pub fn new(
name: impl Into<String>,
nodes: Vec<BlueprintNode>,
@@ -409,7 +409,7 @@ ascii-dag = "0.9.1"
//! The `aura graph` ASCII-DAG adapter (#13): turns the engine's graph-as-data
//! (C9) into an `ascii_dag::Graph` rendered to a `String`. Two views:
//! `render_blueprint` draws composites as named cluster boxes (pre-inline);
//! `render_compilat` draws the flat post-inline graph (boundaries dissolved,
//! `render_flat_graph` draws the flat post-inline graph (boundaries dissolved,
//! C23). Rendering reads structure + node `label()`s only — never `eval`.
//!
//! ascii-dag borrows its node/subgraph labels as `&'a str`, so each function
@@ -541,7 +541,7 @@ pub fn render_blueprint(bp: &Blueprint) -> String {
/// Compiled view: the flat post-inline graph (no clusters; boundaries dissolved,
/// C23). Each `Box<dyn Node>` labels itself; node display id = node index.
pub fn render_compilat(nodes: &[Box<dyn Node>], sources: &[SourceSpec], edges: &[Edge]) -> String {
pub fn render_flat_graph(nodes: &[Box<dyn Node>], sources: &[SourceSpec], edges: &[Edge]) -> String {
let mut labels: Vec<String> = nodes.iter().map(|n| n.label()).collect();
let source_base = labels.len();
for src in sources {
@@ -669,7 +669,7 @@ to:
let bp = sample_blueprint();
let out = if compiled {
let (nodes, sources, edges) = bp.compile().expect("valid sample blueprint");
graph::render_compilat(&nodes, &sources, &edges)
graph::render_flat_graph(&nodes, &sources, &edges)
} else {
graph::render_blueprint(&bp)
};
@@ -729,7 +729,7 @@ In `crates/aura-cli/src/main.rs`, inside the existing `#[cfg(test)] mod tests` (
fn compiled_view_dissolves_the_composite_boundary() {
let bp = sample_blueprint();
let (nodes, sources, edges) = bp.compile().expect("valid sample");
let out = graph::render_compilat(&nodes, &sources, &edges);
let out = graph::render_flat_graph(&nodes, &sources, &edges);
// node labels survive inlining...
assert!(out.contains("SMA(2)") && out.contains("SMA(4)"), "labels lost:\n{out}");
// ...but the composite cluster name does NOT (boundary dissolved, C23)
@@ -748,7 +748,7 @@ In `crates/aura-cli/src/main.rs`, inside the existing `#[cfg(test)] mod tests` (
- [ ] **Step 2: Run the tests to verify they pass**
Run: `cargo test -p aura-cli blueprint_view_shows_cluster_and_param_labels compiled_view_dissolves_the_composite_boundary swapped_sma_inputs_render_differently`
Expected: PASS (all three). (`render_blueprint`/`render_compilat`/`sample_blueprint` already exist from Task 4; this task only adds tests + the test-only swapped builder.)
Expected: PASS (all three). (`render_blueprint`/`render_flat_graph`/`sample_blueprint` already exist from Task 4; this task only adds tests + the test-only swapped builder.)
- [ ] **Step 3: Freeze the full-byte golden snapshots**
@@ -773,7 +773,7 @@ Then run: `cargo run -q -p aura-cli -- graph --compiled`, and add the twin:
fn compiled_view_golden() {
let bp = sample_blueprint();
let (nodes, sources, edges) = bp.compile().expect("valid sample");
let out = graph::render_compilat(&nodes, &sources, &edges);
let out = graph::render_flat_graph(&nodes, &sources, &edges);
let expected = "<<paste the exact captured `aura graph --compiled` stdout here>>";
assert_eq!(out, expected, "compiled render drifted; re-capture if intended");
}
+3 -3
View File
@@ -14,7 +14,7 @@ params into one flat, path-qualified, inspectable param-space.
declare their tunable knobs. `aura-engine` gains a read-only `Blueprint::param_space()`
that walks the graph-as-data (using the already-public `Composite::name()` as path
prefix) in the same deterministic depth-first order `lower_items` uses — a parallel
projection that leaves `compile`/`inline_composite` (and the compilat) untouched.
projection that leaves `compile`/`inline_composite` (and the flat graph) untouched.
**Tech Stack:** Rust workspace — `aura-core` (node contract), `aura-std` (7 nodes),
`aura-engine` (blueprint/compile). Param identity is positional (slot), name a
@@ -320,7 +320,7 @@ the build names any remaining `NodeSchema` literal missing `params`, add
Run: `cargo test -p aura-engine`
Expected: PASS — all pre-existing tests (incl. the bit-identical
`composite_sma_cross_runs_bit_identical_to_hand_wired` and the golden render tests)
stay green: the compilat is unchanged, only schema literals gained an empty field.
stay green: the flat graph is unchanged, only schema literals gained an empty field.
---
@@ -515,7 +515,7 @@ and `LinComb::schema` are clippy-clean).
- **Do not touch** `compile`, `inline_composite`, `lower_items`, or any edge/wiring
logic. `param_space()` is a *parallel* read-only projection that mirrors the
inliner's traversal order; it must not share or alter it. The spec's correctness
rests on the compilat staying bit-identical (every existing golden / bit-identical
rests on the flat graph staying bit-identical (every existing golden / bit-identical
test stays green by construction).
- **fieldtests/ are out of scope** — they are excluded crates (own `Cargo.toml`),
not in `--workspace`, and are frozen cycle-archive snapshots. They construct the
+3 -3
View File
@@ -467,7 +467,7 @@ param-driven path plus no-param wrappers. The arity is checked up front via
self.compile_with_params(&[])
}
/// Compile under an injected vector, then hand the flat compilat to the
/// Compile under an injected vector, then hand the flat graph to the
/// unchanged `Harness::bootstrap`.
pub fn bootstrap_with_params(self, params: Vec<Scalar>) -> Result<Harness, CompileError> {
let (nodes, sources, edges) = self.compile_with_params(&params)?;
@@ -640,7 +640,7 @@ and inside the composite loop:
}
```
(`render_compilat` / the compiled-view renderer operates on built flat nodes via
(`render_flat_graph` / the compiled-view renderer operates on built flat nodes via
`Node::label()` and is unchanged.)
- [ ] **Step 2: Sample blueprint → factories (`main.rs`)**
@@ -665,7 +665,7 @@ and inside the composite loop:
(`main.rs:230`): the blueprint view is now param-generic and identical for both
orderings, so the swap is not observable there. Re-express the swap as a different
injected vector (`vec![Scalar::I64(4), Scalar::I64(2), Scalar::F64(0.5)]`) and
assert the **compiled** view differs (`render_compilat` of the compiled flat nodes
assert the **compiled** view differs (`render_flat_graph` of the compiled flat nodes
shows `SMA(4)`/`SMA(2)` swapped), not the blueprint view. Rename the test to its
new premise (e.g. `swapped_param_vector_changes_the_compiled_render`).
@@ -14,7 +14,7 @@ top-level item — a composite labelled by `name()`, opaque) plus a definitions
block where each distinct composite type (deduped by `name()`, collected
recursively) renders its interior once with `[in:k]`/`[out]` port markers. The
old `ItemDisplay`/`producer_id`/`consumer_ids`/subgraph machinery and the
nested-composite `unimplemented!` are removed. `render_compilat` is untouched.
nested-composite `unimplemented!` are removed. `render_flat_graph` is untouched.
**Tech Stack:** `crates/aura-cli/src/graph.rs` (render), `crates/aura-cli/src/main.rs`
(tests), `ascii-dag` flat `RenderMode::Vertical`, the `aura_engine`
@@ -161,7 +161,7 @@ imports (lines 12-14) with:
//! views. `render_blueprint` shows the authored structure — a flat main graph
//! wiring the harness with each composite as a single opaque node, plus a
//! `where:` section that defines each distinct composite type once (its interior
//! with `[in:k]`/`[out]` port markers). `render_compilat` shows the flat
//! with `[in:k]`/`[out]` port markers). `render_flat_graph` shows the flat
//! post-inline graph (boundaries dissolved, C23). Rendering reads structure +
//! node `label()`s only — never `eval`.
//!
@@ -178,7 +178,7 @@ use aura_engine::{Blueprint, BlueprintNode, Composite, Edge, SourceSpec};
(`Composite` is added — named by the new helpers; `Target` is dropped — no longer
named after the `ItemDisplay` collapse. `Edge`/`SourceSpec`/`Node` remain: they are
`render_compilat`'s parameter types.)
`render_flat_graph`'s parameter types.)
- [ ] **Step 4: Rewrite `graph.rs` — replace `ItemDisplay`/`producer_id`/`consumer_ids`/`render_blueprint` (old lines 16-132)**
@@ -233,7 +233,7 @@ pub fn render_blueprint(bp: &Blueprint) -> String {
}
/// Build and render a flat (no-subgraph) ascii-dag graph from owned labels and
/// edge pairs — the same idiom `render_compilat` uses.
/// edge pairs — the same idiom `render_flat_graph` uses.
fn render_flat(labels: &[String], edges: &[(usize, usize)]) -> String {
let mut g = Graph::with_mode(RenderMode::Vertical);
for (id, l) in labels.iter().enumerate() {
@@ -376,7 +376,7 @@ Run: `cargo test --workspace`
Expected: PASS, 0 failed. In particular the four must-stay-green tests
(`compiled_view_dissolves_the_composite_boundary`, `compiled_view_golden`,
`swapped_param_vector_changes_the_compiled_render`,
`run_sample_is_deterministic_and_non_trivial`) remain green — `render_compilat` is
`run_sample_is_deterministic_and_non_trivial`) remain green — `render_flat_graph` is
untouched, so `compiled_view_golden` is byte-identical.
Run: `cargo clippy --workspace --all-targets -- -D warnings`
@@ -15,7 +15,7 @@ composite boundary (`NodeSchema.output: Vec<FieldSpec>`, `Edge::from_field`
selects a column, leaf multi-output already works). This cycle replaces the
single `OutPort` with a `Vec<OutField>` and lifts the three `field == 0` caps at
the boundary (`inline_composite` nested arm, `rewrite_edge` composite arm). Names
live at the blueprint boundary only and are dropped in the compilat (C23); C8/C7/C4
live at the blueprint boundary only and are dropped in the flat graph (C23); C8/C7/C4
are untouched — one port, one row, K columns.
**Tech Stack:** `crates/aura-engine/src/blueprint.rs` (type + compile logic),
@@ -72,7 +72,7 @@ with:
/// One re-exported field of a composite's output record: an interior
/// `(node, output-field)` surfaced at the boundary under `name`. `name` is a
/// non-load-bearing render/debug symbol (C23) — like `FieldSpec.name` and
/// `Composite.name`, it does not reach the compilat.
/// `Composite.name`, it does not reach the flat graph.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OutField {
pub node: usize,
@@ -547,14 +547,14 @@ Run: `cargo test -p aura-cli blueprint_view_golden`
Expected: FAIL first (golden drift: `[out]``[out:cross]`), then PASS after the
literal is re-captured.
- [ ] **Step 9: Confirm the compilat golden is byte-identical (main.rs:505530)**
- [ ] **Step 9: Confirm the flat graph golden is byte-identical (main.rs:505530)**
`compiled_view_golden` pins the flat compilat render; per C23 names are dropped at
`compiled_view_golden` pins the flat graph render; per C23 names are dropped at
inline, so it must **not** change (acceptance criterion 6 — the regression guard).
Run: `cargo test -p aura-cli compiled_view_golden`
Expected: PASS with **no** edit to the golden literal. If it drifts, a name leaked
into the compilat — a bug to fix, not a golden to re-capture.
into the flat graph — a bug to fix, not a golden to re-capture.
- [ ] **Step 10: Full CLI test + clippy**
@@ -617,7 +617,7 @@ Expected: PASS — zero warnings.
- [ ] `cargo test --workspace` green (incl. the four new engine capability tests and the re-captured `blueprint_view_golden`).
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` green.
- [ ] `rg "OutPort" --type rust` returns nothing (type fully retired).
- [ ] `compiled_view_golden` unchanged (compilat byte-identical — C23 / acceptance criterion 6).
- [ ] `compiled_view_golden` unchanged (flat graph byte-identical — C23 / acceptance criterion 6).
- [ ] The MACD PoC re-exports `macd`/`signal`/`histogram`; the run still trades the histogram (via `from_field: 2`).
The implementation commit closes #40.
+1 -1
View File
@@ -668,7 +668,7 @@ literal (do not hand-edit field-by-field). Re-run: PASS.
Run: `cargo test -p aura-cli compiled_view_golden`
Expected: PASS with NO edit to the golden (`main.rs:510-535`). The boundary names
never reach the compilat (C23); if this golden drifts, STOP — that is a bug, not a
never reach the flat graph (C23); if this golden drifts, STOP — that is a bug, not a
golden to re-capture.
- [ ] **Step 9: Confirm MACD run determinism is unchanged**
+2 -2
View File
@@ -419,7 +419,7 @@ fallback path is the unchanged positional branch.)
- [ ] **Step 7: Confirm `compiled_view_golden` is byte-stable**
Run: `cargo test -p aura-cli compiled_view_golden`
Expected: PASS unchanged — the compilat carries no aliases / `#` identifiers
Expected: PASS unchanged — the flat graph carries no aliases / `#` identifiers
(C23 guard). If it fails, the render change leaked into the compiled view — stop
and inspect; do not re-capture this golden.
@@ -636,7 +636,7 @@ signatures (type initial + alias initials + recursive input signatures) — do n
hide an unnamed configuration axis: a collision is a `CompileError`
(`IndistinguishableFanIn`) when at least one colliding source carries an
unaliased param slot. Genuinely-interchangeable sources (equal signatures, no
param) stay legal. Construction-phase only; the compilat stays name-free (C23).
param) stay legal. Construction-phase only; the flat graph stays name-free (C23).
The graph view renders each fan-in input as the shortest sibling-unique prefix
of its source signature.
```
@@ -482,7 +482,7 @@ In `crates/aura-engine/src/harness.rs`, next to `SourceSpec` (≈49), add:
```rust
/// The flat, type-erased, index-wired output of `Composite::compile` — the target
/// `Harness::bootstrap` consumes (C23's "compilat", now a named type). `signatures[i]`
/// `Harness::bootstrap` consumes (C23's "flat graph", now a named type). `signatures[i]`
/// is the static signature of `nodes[i]` (gathered from each primitive's builder at
/// lowering); `bootstrap` reads kinds/output from it and `nodes[i].lookbacks()` for
/// buffer depth. `sources` are the lowered bound roles, in role-declaration order.
@@ -1093,7 +1093,7 @@ is preserved). `macd_blueprint` (264-) return type → `Composite`.
and `Harness::bootstrap(nodes, sources, edges)` → `Harness::bootstrap(flat)`.
`render_compiled` (336-337): signature `bp: Blueprint` → `bp: Composite`; body
`let flat = bp.compile_with_params(point).expect("valid blueprint"); graph::render_compilat(&flat.nodes, &flat.sources, &flat.edges, color)`.
`let flat = bp.compile_with_params(point).expect("valid blueprint"); graph::render_flat_graph(&flat.nodes, &flat.sources, &flat.edges, color)`.
`run_sample`/`sample_harness` (the bootstrap at main.rs:53): same `FlatGraph`
threading as `run_macd`.
@@ -1114,7 +1114,7 @@ In `crates/aura-cli/src/graph.rs`:
- Match arms `BlueprintNode::Leaf` → `Primitive` at **118, 212-213, 325, 337, 399-400**.
- `factory: &LeafFactory` param at **250** → `&PrimitiveBuilder` (its `.params()`/
`.label()` calls are unchanged).
- The `sources: &[SourceSpec]` param at 482 (render_compilat) is unchanged
- The `sources: &[SourceSpec]` param at 482 (render_flat_graph) is unchanged
(`FlatGraph.sources` is still `Vec<SourceSpec>`).
- [ ] **Step 5: Migrate aura-cli's `#[cfg(test)]` module**
+2 -2
View File
@@ -15,7 +15,7 @@ the CLI render layer: (A) thread the root composite as the fan-in stub context
(`stub_ctx` drops its `Option`; both `render_graph` callers pass `&Composite`),
and (B) build root entries from `role.name` instead of `format!("source:{kind}")`.
The existing `slot_source`/`fan_in_identifiers`/`signature_of` are reused verbatim
(no new stub path). `render_compilat` is untouched (the compiled view keeps
(no new stub path). `render_flat_graph` is untouched (the compiled view keeps
`[source:F64]` — names dissolve post-inline, C23). Behaviour-preserving for the
run path (C1); read-only render (C9); no engine change.
@@ -50,7 +50,7 @@ captured from the live `aura graph` / `aura graph --macd` output.
add a `[src]` role-name-passthrough assertion.
**Explicitly OUT of scope (do not touch):**
- `crates/aura-cli/src/graph.rs:486-505` (`render_compilat`) — the negative-control
- `crates/aura-cli/src/graph.rs:486-505` (`render_flat_graph`) — the negative-control
path; must stay byte-identical so `compiled_view_golden` does not move.
- `crates/aura-cli/src/main.rs:564-589` (`compiled_view_golden`) — its expected
block (`[source:F64]`, `[SimBroker(0.0001)]`) must NOT be edited.
+2 -2
View File
@@ -572,7 +572,7 @@ And delete the `render_compiled` helper (lines 358-363):
/// (C23) view — the shared body of the `--compiled` paths.
fn render_compiled(bp: Composite, point: &[Scalar], color: graph::Color) -> String {
let flat = bp.compile_with_params(point).expect("valid blueprint");
graph::render_compilat(&flat.nodes, &flat.sources, &flat.edges, color)
graph::render_flat_graph(&flat.nodes, &flat.sources, &flat.edges, color)
}
```
If `is_terminal`/`IsTerminal` was imported only for the `color` binding, its `use`
@@ -605,7 +605,7 @@ which dies with the file).
In `crates/aura-cli/src/main.rs`'s `#[cfg(test)] mod tests`, delete these test
functions in full (each asserts retired ascii-dag output and references
`graph::render_blueprint` / `graph::render_compilat` / `graph::Color`):
`graph::render_blueprint` / `graph::render_flat_graph` / `graph::Color`):
```text
blueprint_view_main_graph_shows_composite_as_opaque_node (~409-425)
blueprint_view_defines_each_composite_once (~427-435)
+1 -1
View File
@@ -485,7 +485,7 @@ In the C23-related prose at :359-366 and :813-851: strike the `ParamAlias`
param-overlay from the named-projection set (the param projection is retired;
`Role.name` input roles and `OutField.name` outputs remain). Note that node names
join the same non-load-bearing debug-symbol class (construction-time identity,
dropped at lowering; the compilat stays wired by raw index). Remove the sentence
dropped at lowering; the flat graph stays wired by raw index). Remove the sentence
referencing the dangling-alias `BadInteriorIndex` compile check (:362-364) — that
path is gone with the overlay.
@@ -469,7 +469,7 @@ distinguishability check, if ever wanted, is decoupled from param-space injectiv
At the sentence "Identity is **positional** (the slot, C23 'by index, not name');
… same-type siblings in one composite share a name, uniqueness is at the slot",
add the refinement: identity in the **compilat** stays positional (C23, unchanged),
add the refinement: identity in the **flat graph** stays positional (C23, unchanged),
but the `param_space()` **name projection** — the authoring / by-name address space
— must be **injective** for a blueprint to compile (cycle 0032). Two layers:
positional wiring below, injective name address space above.
@@ -477,9 +477,9 @@ positional wiring below, injective name address space above.
- [ ] **Step 3: Note the C23 amendment**
Record that the injectivity check is part of bootstrap-as-compilation; node names
stay non-load-bearing (dropped at lowering, compilat wired by raw index). The check
stay non-load-bearing (dropped at lowering, flat graph wired by raw index). The check
reads the boundary name projection; it does not make names load-bearing in the
compilat.
flat graph.
- [ ] **Step 4: Verify the ledger still builds as docs (sanity)**
@@ -16,7 +16,7 @@ resolves every name against each node's cached `NodeSchema` by exactly-one-match
(the `PrimitiveBuilder::bind` posture, but fallible) and hands the resolved
`Vec<Edge>`/`Vec<Role>`/`Vec<OutField>` to the unchanged `Composite::new`. A new
`impl From<Composite> for BlueprintNode` lets `add` accept nested composites.
Names never reach the compilat (C23 holds by construction).
Names never reach the flat graph (C23 holds by construction).
**Tech Stack:** Rust, `aura-engine` (blueprint/harness), `aura-core` (NodeSchema/
PortSpec/FieldSpec/ScalarKind), `aura-std` nodes for tests.
@@ -105,7 +105,7 @@ Create `crates/aura-engine/src/builder.rs` with:
//!
//! A fluent, additive authoring surface that wires a blueprint by typed node
//! handles and port/field *names*, resolving them to the raw-index `Composite`
//! at a single fallible `build()`. The compilat stays index-wired (C23): names
//! at a single fallible `build()`. The flat graph stays index-wired (C23): names
//! are resolved at the authoring boundary and never reach `FlatGraph` — the same
//! posture param-name resolution already has (`Binder`, blueprint.rs). Resolution
//! mirrors `PrimitiveBuilder::bind`'s collect-then-reject (node.rs), but returns
+2 -2
View File
@@ -324,7 +324,7 @@ Expected: PASS (`1 passed`).
deliberately under-wired `strategy` (LinComb's two `term` inputs never fed) only to
read its flat param order. Fully wire it: fan `fast_slow`'s output into both LinComb
terms, and add a covering root role for `strategy`'s `price` input. Wiring changes no
node and no param, so the asserted `space == from_compilat` equality is unchanged.
node and no param, so the asserted `space == from_flat` equality is unchanged.
**Files:**
- Modify: `crates/aura-engine/src/blueprint.rs:1877`
@@ -611,7 +611,7 @@ never consumers, so only interior input slots are subject to the rule. There is
optional-input concept**: every declared input port is required (no shipped node runs
meaningfully without one; an unwarmed mode-A input is *wired-but-not-yet-valued*, not
unwired). The check is **index-based and name-free** — it touches no name machinery
and emits nothing into the compilat, so C23 is untouched (it proves the existing
and emits nothing into the flat graph, so C23 is untouched (it proves the existing
raw-index wiring is total and single-valued). Inherited identically by the raw
`Composite::new` path and the `GraphBuilder::build()` path (both compile via
`compile_with_params`).
+17 -17
View File
@@ -1,4 +1,4 @@
# Blueprint → compilat: composite inlining — Design Spec
# Blueprint → flat graph: composite inlining — Design Spec
**Date:** 2026-06-05
**Status:** Draft — awaiting user spec review
@@ -8,13 +8,13 @@
Introduce the **construction layer** that turns a hand-written, raw-index graph
into named **graph-as-data** (a `Blueprint`) that **compiles** to the flat,
type-erased instance the run loop already runs (the **compilat**). The unit of
type-erased instance the run loop already runs (the **flat graph**). The unit of
reuse is the **composite**: a nestable sub-graph fragment that exposes one output
port and named input roles, and that the compile step **inlines** into the flat
`(nodes, sources, edges)` the unchanged `Harness::bootstrap` consumes.
This realizes C9 (fractal composition), C19 (bootstrap = compilation), and C23
(blueprint → flat compilat, wired by raw index; names non-load-bearing). It is the
(blueprint → flat graph, wired by raw index; names non-load-bearing). It is the
anchor of the *Construction layer* milestone (Gitea #12).
**What ships:** a `Blueprint` / `Composite` representation in `aura-engine`, a
@@ -51,9 +51,9 @@ edges *into* a composite are resolved through its input roles (a fan-out: one
blueprint edge may become several flat edges), and edges *out of* a composite are
resolved to its interior output port. Nesting is handled by compiling
inside-out / recursively. After lowering, `bootstrap`'s existing kind-check and
Kahn cycle-check cover the flat compilat for free (C7/C8).
Kahn cycle-check cover the flat graph for free (C7/C8).
The compilat is **wired by raw index, not by name** (C23): the composite boundary
The flat graph is **wired by raw index, not by name** (C23): the composite boundary
dissolves entirely; field/input-role names, where kept, are non-load-bearing debug
symbols (as `FieldSpec.name` already is, `crates/aura-core/src/node.rs:37`).
@@ -198,20 +198,20 @@ pub struct Blueprint {
impl Blueprint {
pub fn new(nodes: Vec<BlueprintNode>, sources: Vec<SourceSpec>,
edges: Vec<Edge>) -> Self;
/// Lower to the flat compilat: inline every composite (recursive), offset
/// Lower to the flat graph: inline every composite (recursive), offset
/// interior indices, rewrite edges, fan input roles out. Run loop unchanged.
pub fn compile(self) -> Result<(Vec<Box<dyn Node>>, Vec<SourceSpec>, Vec<Edge>), CompileError>;
/// Compile, then hand the flat compilat to the unchanged Harness::bootstrap.
/// Compile, then hand the flat graph to the unchanged Harness::bootstrap.
pub fn bootstrap(self) -> Result<Harness, CompileError>;
}
/// A construction-phase fault, caught before the flat compilat reaches bootstrap.
/// A construction-phase fault, caught before the flat graph reaches bootstrap.
#[derive(Debug, PartialEq, Eq)]
pub enum CompileError {
BadInteriorIndex, // an interior edge/role/output index is out of range
RoleKindMismatch { role: usize }, // a role fans into interior slots of differing kinds
OutputPortOutOfRange, // the output port names a missing interior node/field
Bootstrap(BootstrapError), // the lowered flat compilat failed bootstrap's checks
Bootstrap(BootstrapError), // the lowered flat graph failed bootstrap's checks
}
```
@@ -232,7 +232,7 @@ trait are **unchanged**.
items at an offset, rewrite interior edges, resolve edges into a composite via
its input roles (fan-out), resolve edges out of a composite to its output port.
- **`CompileError`** — construction-phase faults, with `Bootstrap(BootstrapError)`
for the lowered-compilat failure.
for the lowered flat-graph failure.
## Data flow
@@ -251,10 +251,10 @@ Authoring → `Blueprint` (graph-as-data, blueprint indices) → `compile()`:
`from: base_c + output.node`, `from_field: output.field`.
4. Result: flat `(nodes, sources, edges)` with no composites, wired by raw index.
`Blueprint::bootstrap` hands these to the unchanged `Harness::bootstrap`, whose
kind-check + Kahn cycle-check (`harness.rs:142-192`) validate the compilat.
kind-check + Kahn cycle-check (`harness.rs:142-192`) validate the flat graph.
Then `Harness::run` drives the flat compilat exactly as today (C1 determinism
preserved — the compilat is the same flat topology the hand-wiring produced).
Then `Harness::run` drives the flat graph exactly as today (C1 determinism
preserved — the flat graph is the same flat topology the hand-wiring produced).
## Error handling
@@ -263,10 +263,10 @@ preserved — the compilat is the same flat topology the hand-wiring produced).
(`BadInteriorIndex`), a role fanning into interior slots of differing scalar
kinds (`RoleKindMismatch`), an output port naming a missing interior node/field
(`OutputPortOutOfRange`).
- The lowered flat compilat is validated by `bootstrap`'s **existing** checks; any
- The lowered flat graph is validated by `bootstrap`'s **existing** checks; any
`BootstrapError` (kind mismatch, bad index, cycle) is wrapped as
`CompileError::Bootstrap(..)`. No duplication of bootstrap's validation in the
inliner — the compilat is checked once, where it always was (C7/C8).
inliner — the flat graph is checked once, where it always was (C7/C8).
- A composite's *derived* `schema()` is what the enclosing graph kind-checks
against; a same-name role fanning to mismatched kinds is caught at compile, not
hidden until run.
@@ -278,7 +278,7 @@ preserved — the compilat is the same flat topology the hand-wiring produced).
an edge out of a composite resolves to its output port; a **nested** composite
(composite-in-composite) inlines correctly.
- **Unit (errors):** `BadInteriorIndex`, `RoleKindMismatch`, `OutputPortOutOfRange`
each provoked by a malformed composite; a lowered compilat with a kind mismatch
each provoked by a malformed composite; a lowered flat graph with a kind mismatch
or cycle surfaces as `CompileError::Bootstrap(..)`.
- **Integration (headline, C1):** the SMA-cross composite, compiled and run,
produces equity + exposure sink traces **bit-identical** to the hand-wired flat
@@ -307,7 +307,7 @@ possible once params and the sweep orchestration land (C12/C21).
4. The SMA-cross composite produces a run **bit-identical** to the hand-wired
graph (C1) — the headline test passes.
5. `compile()` rejects malformed wiring with a typed `CompileError`; the lowered
flat compilat is validated by `bootstrap`'s existing kind/cycle checks (no
flat graph is validated by `bootstrap`'s existing kind/cycle checks (no
re-implementation).
6. `Harness::bootstrap`, `Harness::run`, `Edge`, `Target`, `SourceSpec`, and the
`Node` trait are **unchanged**; the run loop is untouched.
+9 -9
View File
@@ -22,7 +22,7 @@ Two views are rendered, selectable:
- `aura graph` — the **blueprint** view: the authored graph *before* inlining,
with each composite drawn as a labelled cluster box (C9 fractal structure as
written).
- `aura graph --compiled` — the **compilat** view: the flat, post-inline
- `aura graph --compiled` — the **flat graph** view: the flat, post-inline
`(nodes, sources, edges)` that actually runs; composite boundaries are
dissolved (C23). This is the same graph the run loop and the deferred C23
optimiser see.
@@ -54,7 +54,7 @@ reason the work is split across crates rather than landing in one:
*with its parameters*, so two `Sma` nodes read `SMA(2)` and `SMA(4)` and a
swap is visible. The method is additive, non-load-bearing, and never read by
the run loop — a C8 refinement aligned with C23, not a behavioural change. It
must live on `Node` (not a side table) because the compilat view holds
must live on `Node` (not a side table) because the flat graph view holds
`Vec<Box<dyn Node>>` and labels each node by asking the trait object directly.
## Concrete code shapes
@@ -151,7 +151,7 @@ pub trait Node {
The default takes `&self` and returns an owned `String`, so `Node` stays
object-safe and `Box<dyn Node>::label()` dispatches (a `type_name::<Self>()`
default would require `Self: Sized` and is therefore rejected — it would not be
callable on the `dyn Node` the compilat holds).
callable on the `dyn Node` the flat graph holds).
**2. aura-std overrides — one shown, the rest analogous (aura-std/src/sma.rs).**
@@ -221,7 +221,7 @@ use ascii_dag::graph::{Graph, RenderMode};
// Compiled view: trivial — compile(), then one display node per flat node, one
// edge per flat edge, each label from the boxed node itself.
pub fn render_compilat(nodes: &[Box<dyn Node>], sources: &[SourceSpec],
pub fn render_flat_graph(nodes: &[Box<dyn Node>], sources: &[SourceSpec],
edges: &[Edge]) -> String {
let labels: Vec<String> = /* source labels ++ nodes.iter().map(|n| n.label()) */;
let mut g = Graph::with_mode(RenderMode::Vertical);
@@ -236,7 +236,7 @@ pub fn render_compilat(nodes: &[Box<dyn Node>], sources: &[SourceSpec],
// leaves render inside it; blueprint-level edges/source-targets resolve through
// composite input-roles (fan) and output ports to interior display ids — the
// same boundary resolution compile() performs, but emitting cluster-grouped
// display nodes instead of a flat compilat.
// display nodes instead of a flat graph.
pub fn render_blueprint(bp: &Blueprint) -> String { /* … */ }
```
@@ -252,7 +252,7 @@ match args.next().as_deref() {
let bp = sample_blueprint();
let out = if compiled {
let (nodes, sources, edges) = bp.compile().expect("valid sample blueprint");
graph::render_compilat(&nodes, &sources, &edges)
graph::render_flat_graph(&nodes, &sources, &edges)
} else {
graph::render_blueprint(&bp)
};
@@ -296,7 +296,7 @@ fn swapped_sma_inputs_render_differently() {
internal test call sites. No dependency added.
- **aura-cli** (`Cargo.toml`, `src/main.rs`, new `src/graph.rs`): the
`ascii-dag` dependency; `sample_blueprint()` (+ a swapped variant for the
test); the `graph` module with `render_blueprint` / `render_compilat`; the
test); the `graph` module with `render_blueprint` / `render_flat_graph`; the
`graph` dispatch arm; the rendering tests.
## Data flow
@@ -308,7 +308,7 @@ edges/source-targets through composite roles/output to interior display ids,
labels each leaf via `node.label()``Graph::render()``println!` to stdout.
`aura graph --compiled``sample_blueprint()``bp.compile()` yields the flat
`(nodes, sources, edges)``render_compilat(...)` assigns one display id per
`(nodes, sources, edges)``render_flat_graph(...)` assigns one display id per
source and per flat node, draws the flat edges, labels each `Box<dyn Node>` via
`.label()``Graph::render()` → stdout. Composite boundaries are gone (C23):
the cluster boxes of the blueprint view are absent, by design.
@@ -365,7 +365,7 @@ commitment* — and is met:
C1 determinism are untouched (`label()` is never read during a run); the C8
node contract is *refined*, not broken (the addition is the non-load-bearing
render symbol C23 already reserves and cites #13 for); composites still inline
to a name-free index-wired compilat (C23 — the compiled view demonstrates it).
to a name-free index-wired flat graph (C23 — the compiled view demonstrates it).
Issue #13's own acceptance checklist is satisfied: `ascii-dag` added as the
rendering crate's dependency; a `graph → ascii_dag::Graph` adapter in Vertical
+5 -5
View File
@@ -44,10 +44,10 @@ Two layers, both resting on machinery that already exists:
the deterministic depth-first item order that `lower_items` (the inliner)
already uses. **`compile`/`inline_composite` are not touched**: the param-space
is a separate read-only projection of the blueprint (a further C9
introspection, like `nodes()`), not a change to the flat compilat.
introspection, like `nodes()`), not a change to the flat graph.
The **identity** of a param is **positional** — its slot index in the flat
param-space — exactly as the compilat wires by raw index, not by name (C23). The
param-space — exactly as the flat graph wires by raw index, not by name (C23). The
name is a **non-load-bearing debug symbol**, path-qualified at aggregation time
(`strategy.combine.weights[0]`) so a reader can trace a knob back to its node.
@@ -270,7 +270,7 @@ whose kind could mismatch (that check lands with the bind in #31).
param-space; an empty blueprint yields an empty space.
Build/test/clippy green across the workspace; existing compile/bootstrap/run and
render tests stay green (the compilat is unchanged — `compile`/`inline_composite`
render tests stay green (the flat graph is unchanged — `compile`/`inline_composite`
are not touched, so every existing bit-identical and golden-snapshot test is
unaffected by construction).
@@ -292,7 +292,7 @@ applies, read against aura's domain invariants:
Determinism (C1) holds — aggregation order is a pure structural function.
Topology-invariance (C19) holds — a vector knob's arity `N` is topology-fixed
(it equals the node's input arity), declared as `N` fixed entries, never a
sweep param. The compilat is untouched (C23) — params are a read-only
sweep param. The flat graph is untouched (C23) — params are a read-only
projection, not a change to the wired graph; every existing bit-identical test
stays green by construction.
@@ -300,7 +300,7 @@ applies, read against aura's domain invariants:
- **Param-set binding / injection (#31)** — binding an injected typed value-vector
to the param-space slot-by-slot, with the kind-check that declaration defers.
- **Sweep enumeration (#32)** — building a family of compilats over the
- **Sweep enumeration (#32)** — building a family of flat graphs over the
param-space.
- **The run-supplied search-range (#32 / C20)** — which subset/grid a run sweeps
lives in the experiment-builder, not in the node (the param declaration carries
+1 -1
View File
@@ -302,7 +302,7 @@ not the node (C20, established at #30's close), and full domain validation is
- **Bit-identity (the central proof).** `bootstrap_with_params(vec![I64(2),
I64(4), F64(0.5)])` on the SMA-cross harness produces equity + exposure traces
bit-identical to the hand-wired `sma_cross(2, 4)` graph — build-then-wire does
not change the compilat for a given point. (Re-expresses
not change the flat graph for a given point. (Re-expresses
`composite_sma_cross_runs_bit_identical_to_hand_wired`.)
- **Injection is observable.** A different vector (`[I64(5), I64(20), F64(1.0)]`)
yields a different, populated trace from the same blueprint — distinct instances,
@@ -20,7 +20,7 @@ reused composite's body once, removes the current nested-composite `unimplemente
and renders on ascii-dag's **flat** layout only — sidestepping the cluster-layout
bug at the root.
The **compiled view** (`render_compilat`, flat fully-inlined, C23) is unchanged,
The **compiled view** (`render_flat_graph`, flat fully-inlined, C23) is unchanged,
byte-for-byte.
## Architecture
@@ -30,10 +30,10 @@ Two ontological tiers map onto two render shapes:
| View | What it shows | Mechanism |
|------|---------------|-----------|
| Blueprint (`render_blueprint`) | authored source: harness wiring + composites as named subroutines, each body rendered once | main flat graph (composite = opaque node) + a `where:` block of per-type definitions |
| Compiled (`render_compilat`) | inlined machine form, boundaries dissolved (C23) | one flat graph (unchanged) |
| Compiled (`render_flat_graph`) | inlined machine form, boundaries dissolved (C23) | one flat graph (unchanged) |
The redesign is **CLI-render-only**. No change to the engine, the Node/Blueprint
data model, or `render_compilat`. The blueprint data model already carries
data model, or `render_flat_graph`. The blueprint data model already carries
everything needed (`Composite::name/nodes/edges/input_roles/output`,
`Blueprint::nodes/sources/edges`).
@@ -47,7 +47,7 @@ everything needed (`Composite::name/nodes/edges/input_roles/output`,
`Barycentric`/`BrandesKopf` are unimplemented stubs; `render()` takes no config).
The **flat** layout reserves `width + 3` per node and is collision-free at all
widths. The new model renders only flat graphs, so the bug cannot arise.
2. **Scale.** The blueprint is authored structure, not the compilat. A composite
2. **Scale.** The blueprint is authored structure, not the flat graph. A composite
with 50 interior nodes is **one** node in the main graph; blueprint size tracks
top-level wiring, not inlined node count. Real strategies stay displayable.
3. **Render-once.** A composite reused N times appears N times as an opaque node in
@@ -182,7 +182,7 @@ fn render_definition(c: &Composite) -> String {
`where:` block. Uses only flat ascii-dag graphs.
- **`render_flat(labels, edges) -> String`** (new internal helper, or inline). A
thin wrapper over the existing flat-graph build (`Graph::with_mode(Vertical)`,
`add_node`, `add_edge`, `render`) — exactly what `render_compilat` already does,
`add_node`, `add_edge`, `render`) — exactly what `render_flat_graph` already does,
with **no** `add_subgraph` / `put_nodes`.
- **`collect_distinct_composites(bp) -> Vec<&Composite>`** (new). Walks top-level
items and, recursively, composite interiors; returns distinct composites in
@@ -191,7 +191,7 @@ fn render_definition(c: &Composite) -> String {
- **`render_definition(c) -> String`** (new). One composite interior → flat graph
with `[in:k]` / `[out]` port markers, prefixed `"<name>:\n"`.
- **`ItemDisplay`**, **`producer_id`**, **`consumer_ids`** (simplified as above).
- **`render_compilat`** — untouched.
- **`render_flat_graph`** — untouched.
## Data flow
@@ -264,7 +264,7 @@ Unchanged and must stay green: `compiled_view_dissolves_the_composite_boundary`,
- `aura graph` (no flag) prints the sample as a small main graph with `[sma_cross]`
opaque, followed by a `where:` block defining `sma_cross` once, on a flat layout
(no `╔═╗` subgraph boxes).
- `aura graph --compiled` output is byte-identical to before (`render_compilat`
- `aura graph --compiled` output is byte-identical to before (`render_flat_graph`
untouched; its golden unchanged).
- The `unimplemented!("nested-composite cluster rendering")` at `graph.rs:75` is
gone; a one-level-nested composite renders.
@@ -50,9 +50,9 @@ boundary; it does not widen the node contract.
base column; the record is a bundle of base columns, as today.
- **C4** (one record per cycle) — unchanged. One `eval`, one row, K columns,
co-fresh by construction.
- **C23** (compilat wired by raw index; names are non-load-bearing debug
- **C23** (flat graph wired by raw index; names are non-load-bearing debug
symbols) — **honoured**: the re-export *names* live at the blueprint boundary
only. The compilat lowering drops them and carries raw `(node, field)` indices,
only. The flat graph lowering drops them and carries raw `(node, field)` indices,
exactly as `FieldSpec.name` and `Composite.name` are dropped at inline today.
A strategy composite still outputs **one** exposure (C10/C7): it is simply a
@@ -144,7 +144,7 @@ pub struct OutPort {
/// One re-exported field of a composite's output record: an interior
/// `(node, output-field)` surfaced at the boundary under `name`. `name` is a
/// non-load-bearing render/debug symbol (C23) — like `FieldSpec.name` and
/// `Composite.name`, it does not reach the compilat.
/// `Composite.name`, it does not reach the flat graph.
pub struct OutField {
pub node: usize,
pub field: usize,
@@ -322,7 +322,7 @@ Authoring → compile → run is unchanged in shape; only the boundary arity wid
3. **Edge rewrite (`rewrite_edge`):** a consumer edge reading the composite with
`from_field = k` resolves to `output[k]` — the k-th re-exported flat
`(node, field)`. Out-of-range `k``BadInteriorIndex`.
4. **Bootstrap:** the flat compilat is unchanged in kind — ordinary
4. **Bootstrap:** the flat graph is unchanged in kind — ordinary
`(from_node, from_field) → (to_node, slot)` edges. Existing per-field
kind-checks at bootstrap (`slot_kind`) apply to each re-exported field as they
already do for any producer field. No new kind rule.
@@ -349,7 +349,7 @@ Unit tests in `blueprint.rs` (alongside the existing compile tests):
1. **Multi-output happy path:** a composite re-exporting 2 interior fields; two
downstream consumers read `from_field: 0` and `from_field: 1`; assert the
compilat wires each consumer to the correct distinct flat `(node, field)`.
flat graph wires each consumer to the correct distinct flat `(node, field)`.
2. **Nested multi-output:** an outer composite re-exporting two fields of an
**inner** multi-output composite (exercises the nested arm's
`nested.get(field)`); assert correct flat resolution.
@@ -383,7 +383,7 @@ preserving).
record; the histogram remains reachable as one selected field.
5. `aura graph` renders K `[out:<name>]` markers per multi-output composite.
6. Single-field output records (every strategy composite, `sma_cross`) compile
and run identically to today — C8 / C7 / C4 untouched; the compilat for an
and run identically to today — C8 / C7 / C4 untouched; the flat graph for an
unchanged blueprint is byte-identical.
7. `cargo build --workspace`, `cargo test --workspace`, and
`cargo clippy --workspace --all-targets -- -D warnings` are green.
+3 -3
View File
@@ -56,13 +56,13 @@ Consequence, made explicit so the implementation cannot drift: a param's identit
in `param_space()` stays its **slot** (position), never its name. Aliases relabel
in place; they never reorder, add, or remove a slot. The existing invariant test
`param_space_mirrors_compiled_flat_node_param_order` (slot order mirrors the flat
compilat) therefore stays green unchanged — it is the C23 anchor for this cycle.
flat graph) therefore stays green unchanged — it is the C23 anchor for this cycle.
### Invariant-neutrality
- **C23 honoured.** Names are non-load-bearing debug symbols dropped at lowering;
identity is positional everywhere (param slot, role index, output field index).
The compilat is byte-identical — `compiled_view_golden` is the regression guard,
The flat graph is byte-identical — `compiled_view_golden` is the regression guard,
exactly as #40 honoured it. The role/param/output *names* never reach a
`Box<dyn Node>`.
- **C8 / C7 / C4 untouched.** This is authoring-surface legibility only: no change
@@ -369,7 +369,7 @@ Engine unit tests (`crates/aura-engine/src/blueprint.rs`):
`param_space()` identical to today's path-qualified factory names.
3. **Slot order invariant (C23 anchor).** `param_space_mirrors_compiled_flat_node_param_order`
stays green unchanged — aliases relabel, never reorder; the slot still mirrors
the flat compilat. (No new test; the existing one is the guard.)
the flat graph. (No new test; the existing one is the guard.)
4. **Out-of-range alias → `BadInteriorIndex`** at `compile_with_params`
(alias `node`/`slot` past the interior leaf's params).
5. **Partial aliasing.** A composite aliasing one of two slots → the aliased slot
@@ -55,7 +55,7 @@ labels — stale prose this cycle corrects.
compile path, and the run are untouched. Files: `graph.rs` (`render_definition`),
`main.rs` (render tests/goldens), `node.rs` (doc comment only).
- **C23.** `compiled_view_golden` is byte-identical — only the blueprint-definition
render changes; the compiled/flat view and the compilat are unaffected. Boundary
render changes; the compiled/flat view and the flat graph are unaffected. Boundary
names still never reach a `Box<dyn Node>`.
- **C8/C7/C4 untouched** — authoring-surface legibility only. `aura run --macd`
is deterministic and unchanged (render-only).
+3 -3
View File
@@ -98,7 +98,7 @@ one composite share a name — uniqueness is at the slot". That stays true for
(C12/C19/C23 untouched). This cycle adds a *separate* authoring well-formedness
rule that bites only at fan-in nodes whose colliding sources have an unnamed
configuration axis. C23 is not violated — the check runs during construction,
before the type-erased compilat; runtime names stay non-load-bearing debug
before the type-erased flat graph; runtime names stay non-load-bearing debug
symbols. What changes is that a composite which was silently accepted (then
rendered ambiguously) is now rejected at compile. This is a deliberate contract
refinement, recorded in the ledger.
@@ -341,7 +341,7 @@ Render (aura-cli):
- **New focused render test**: per-node-call uniqueness, role-name passthrough
(`#price`), the recursive descent (`[Sub(#SEf,#SEu)]`), and the interchangeable
positional fallback (`[Join(#A,#B)]`-style).
- **`compiled_view_golden`** (`main.rs:533`): byte-stable — compilat labels via
- **`compiled_view_golden`** (`main.rs:533`): byte-stable — flat graph labels via
`Box<dyn Node>::label()`, no fan-in stubs, no `#` identifier. C23 guard.
Fixture knock-on (the constraint-driven set):
@@ -364,7 +364,7 @@ Per aura's feature-acceptance: a strategy author cannot accidentally ship a
fan-in whose inputs differ in configuration but share a rendered identity — the
engine rejects it at construction, where aura makes invariants structural;
genuinely-interchangeable inputs stay legal; the graph view communicates input
provenance correctly; no runtime/compilat behaviour changes (C1/C12/C19/C23
provenance correctly; no runtime/flat graph behaviour changes (C1/C12/C19/C23
intact).
- [ ] A composite with a fan-in node whose two sources have equal signatures and
@@ -33,7 +33,7 @@ interior nodes — histogram included. There is no case where an `OutField` warr
a real extra node, so the fix is uniform: fold the name into the producer.
This is **pure render-layer legibility**, not a capability. `OutField.name` is a
non-load-bearing render/debug symbol (C23) that never reaches the compilat; the
non-load-bearing render/debug symbol (C23) that never reaches the flat graph; the
engine, blueprint data model, and bootstrap are untouched. The implementation
commit closes #46.
@@ -80,7 +80,7 @@ test asserts it verbatim.
- **C23 honoured.** `OutField.name` stays a non-load-bearing render symbol dropped
at lowering; output identity is the `(node, field)` position, which survives.
The `:=` form changes only where the name is *printed*, never what it *is*. The
compiled view (`render_compilat`) is name-free and byte-identical —
compiled view (`render_flat_graph`) is name-free and byte-identical —
`compiled_view_golden` is the regression guard, unchanged.
- **C8 / C7 untouched.** No change to the runtime record shape, the four base
scalars, or one-record-per-cycle. This is authoring-surface legibility only.
@@ -244,7 +244,7 @@ The MACD author site (`fn macd` in `main.rs`) and every engine type are
3. **Blueprint render** (`render_definition`): for each interior node, the output
names re-exporting it are folded onto its label as a `:=` binding; no standalone
output node is emitted. Input roles still render as entry nodes.
4. **Compiled render** (`render_compilat`) is name-free and byte-identical
4. **Compiled render** (`render_flat_graph`) is name-free and byte-identical
(`compiled_view_golden`).
## Error handling
+1 -1
View File
@@ -242,7 +242,7 @@ fn field_label(producer_outputs: usize, from_field: usize, name: Option<&str>) -
label. Composite-opaque rendering retained.
- `render_flat` / the `add_edge` call (`graph.rs:81-94`) — gains an optional
per-edge label argument; used by `render_blueprint`, `render_definition`,
and `render_compilat` alike (shared, so the label mechanism is single-owned).
and `render_flat_graph` alike (shared, so the label mechanism is single-owned).
- `leaf_label` — generalised (or paralleled by a top-level variant) to source
param names from `LeafFactory::params()` when there is no `ParamAlias` list.
- a `from_field` → name resolver using `Composite::output()` with an
+5 -5
View File
@@ -63,10 +63,10 @@ a root role is a bound ingestion feed (C3). That filter is semantic, not
cosmetic, and is the sole remaining distinction; the *rendering* of a role is now
identical across levels.
**The compiled view is deliberately left as `[source:F64]`.** `render_compilat`
**The compiled view is deliberately left as `[source:F64]`.** `render_flat_graph`
(graph.rs:495) builds source labels from the flat `SourceSpec.kind` — post-inline
the role *name* has dissolved (C23, names are non-load-bearing debug symbols that
do not reach the compilat), so only the kind survives there. Thus `[price]`
do not reach the flat graph), so only the kind survives there. Thus `[price]`
pre-inline (name alive) and `[source:F64]` post-inline (name gone) is not an
inconsistency — it is C23 made visible, and it is the negative control proving the
change is confined to the structural blueprint view.
@@ -165,7 +165,7 @@ slot stubs in both views (the "root has no context" carve-out is removed).
- **`render_graph` / `leaf_label`** (graph.rs): `stub_ctx` becomes `&Composite`;
the fan-in gate keys on `slots.len() > 1` alone.
- **`render_definition`** (graph.rs): passes `c` instead of `Some(c)` (mechanical).
- **`render_compilat`** (graph.rs): **unchanged** — keeps `source:{kind}` from the
- **`render_flat_graph`** (graph.rs): **unchanged** — keeps `source:{kind}` from the
flat `SourceSpec` (C23).
- **`slot_source` / `fan_in_identifiers` / `signature_of`**: unchanged, reused
verbatim.
@@ -180,7 +180,7 @@ an edge-fed slot resolves through `signature_of(bp.nodes(), bp.edges(),
bp.input_roles(), bp.params(), e.from)`; a role-fed slot returns the role name.
The `where:` definitions still flow through `render_definition(c)`
`render_graph(…, stub_ctx = c)`, untouched and byte-stable. The compiled view
flows through the independent `render_compilat`, untouched.
flows through the independent `render_flat_graph`, untouched.
## Error handling
@@ -217,7 +217,7 @@ All affected tests live in `crates/aura-cli/src/main.rs`:
(acceptance #1 for `--macd` + acceptance #2).
4. **`compiled_view_golden`** (~line 569): asserted **unchanged** — renders
through `render_compilat`, a separate path that keeps `[source:F64]` (C23:
through `render_flat_graph`, a separate path that keeps `[source:F64]` (C23:
name dissolved post-inline) and `[SimBroker(0.0001)]`. The negative control.
5. **`nested_composite_renders_without_panic`** (~437) and
+2 -2
View File
@@ -177,13 +177,13 @@ leaf `#45475a`, composite `#5b4b8a`, source `#2e5a3a`, sink `#5a2e3e`.
### Implementation shape — before → after (secondary)
`crates/aura-cli/src/graph.rs` (the ascii-dag adapter, ~505 lines) is **retired**. Its
public entry points (`render_blueprint`, `render_compilat`) and the `ascii-dag`
public entry points (`render_blueprint`, `render_flat_graph`) and the `ascii-dag`
dependency in `crates/aura-cli/Cargo.toml` are removed.
```rust
// BEFORE — ascii-dag adapter (graph.rs), point-nodes + invented label notation:
pub fn render_blueprint(bp: &Composite, color: Color) -> String { /* ascii_dag::Graph … */ }
pub fn render_compilat(nodes: &[Box<dyn Node>], …) -> String { /* ascii_dag::Graph … */ }
pub fn render_flat_graph(nodes: &[Box<dyn Node>], …) -> String { /* ascii_dag::Graph … */ }
// AFTER — a read-only model serializer (new module, e.g. graph_model.rs),
// hand-rolled deterministic JSON in the RunReport::to_json house style (no serde):
+1 -1
View File
@@ -258,7 +258,7 @@ contributes one `ParamSpec` per param slot, named
prefix by its own name and recurses. Slot order and arity are **unchanged** from
today (the mirror test against the compiled flat-node order stays green); only
the per-knob *string* changes (it gains the node segment and, where a
`ParamAlias` used to relabel, reverts to `<node>.<factory-param>`). The compilat
`ParamAlias` used to relabel, reverts to `<node>.<factory-param>`). The flat graph
remains name-free and wired by raw index (C23): node names are construction-time
identity and debug symbols, dropped at lowering, exactly as alias names were.
@@ -344,14 +344,14 @@ existing resolve → lower → bootstrap flow is unchanged.
injectivity.
- **C12/C19 (255270).** The 0015-era line "same-type siblings in one composite
share a name, uniqueness is at the slot" is now refined: identity in the
**compilat** stays positional (C23, unchanged), but the `param_space()` **name
**flat graph** stays positional (C23, unchanged), but the `param_space()` **name
projection** — the authoring/by-name address space — must be **injective** for a
blueprint to compile. The two layers are distinct: positional wiring below,
injective name address space above.
- **C23.** The injectivity check is part of bootstrap-as-compilation; node names
stay non-load-bearing (dropped at lowering, compilat wired by raw index). The
stay non-load-bearing (dropped at lowering, flat graph wired by raw index). The
check reads the boundary name projection; it does not make names load-bearing in
the compilat.
the flat graph.
## Testing strategy
+6 -6
View File
@@ -59,10 +59,10 @@ closure-wrapping.
against the node's declared `ParamSpec.name`. This follows the C23
authoring-address-space convention as amended in cycle 0032
(`check_param_namespace_injective`): the by-name *authoring* address space is
injective, while the *compilat* stays wired by raw index (C23 unchanged). Name at
the authoring surface, index in the compilat; `.bind()` sits on the authoring
injective, while the *flat graph* stays wired by raw index (C23 unchanged). Name at
the authoring surface, index in the flat graph; `.bind()` sits on the authoring
side and resolves the name to a position immediately — the name is not persisted
into the compilat, so C23's "names are non-load-bearing in the compilat" holds.
into the flat graph, so C23's "names are non-load-bearing in the flat graph" holds.
`.bind()` is **not** the `builder_const(2)` per-node constructor the issue rejects
(which "explodes combinatorially — which subset is constant?"): it is one method,
@@ -227,7 +227,7 @@ Construction (unchanged): `Composite::param_space()` → `collect_params` iterat
bound knob. `compile_with_params(params)` checks arity against the (shrunk)
`param_space`, then `lower_items` slices `n = builder.params().len()` open values
and calls `builder.build(open_slice)`; the wrapped closure reconstructs the full
positional vector and delegates to the node constructor. The compilat is wired by
positional vector and delegates to the node constructor. The flat graph is wired by
raw index exactly as before (C23).
## Error handling
@@ -307,6 +307,6 @@ currently-green test):
`PrimitiveBuilder::bind`.
- Chained and single binds reconstruct the correct positional argument vector for
multi-param nodes (invariant test 3 green).
- C23 holds: the compilat is still wired by raw index; bound param names are
resolved to positions at authoring time and never reach the compilat.
- C23 holds: the flat graph is still wired by raw index; bound param names are
resolved to positions at authoring time and never reach the flat graph.
- No new dependency (C16); no node registry or by-name runtime lookup (C9/C10).
+3 -3
View File
@@ -59,7 +59,7 @@ the standalone HTML):
`name: ` declaration prefix to the box head when a name is present.
**Invariant check.** The name is a render/debug symbol on the model surface
only; it is **not** carried into the compilat (lowering drops it, exactly as
only; it is **not** carried into the flat graph (lowering drops it, exactly as
today — C23). The model gains a deterministic field sourced from an
author-controlled identifier (C14: same input → same model bytes). No hot-path,
determinism, or causality surface is touched. This is the same render/model-only
@@ -294,7 +294,7 @@ cellLabel ── o.name present ⇒ prefix "fast: " + head "SMA" + sig "[lengt
⇒ fast: SMA[length]
```
The name is dropped at lowering (compilat wiring is by raw index, C23); it never
The name is dropped at lowering (flat graph wiring is by raw index, C23); it never
re-enters the engine — there is no runtime path here, only the render/model
surface.
@@ -351,7 +351,7 @@ exported and headlessly drivable), the `named()`/`node_name()` tests above.
- `cargo build --workspace`, `cargo test --workspace`, and
`cargo clippy --workspace --all-targets -- -D warnings` are green; the
`viewer_dot.rs`/`.mjs` guard passes.
- C23 (name dropped at lowering, not in the compilat) and C14 (deterministic
- C23 (name dropped at lowering, not in the flat graph) and C14 (deterministic
model bytes) hold — verified by the unchanged lowering path and the
re-captured goldens.
- Out-of-scope items (collapsed-composite visibility, composite-box prefix, INFO
+1 -1
View File
@@ -67,7 +67,7 @@ param-space")` is exactly that check). Every *unbound* param therefore forces a
sweep axis. Binding `blend.weights[2]` removes it from the param-space, so it
needs no axis — a bound param is a structural constant that drops out of the
tuning surface (C23: `bind` resolves the param *name* to a fixed position; the
compilat is unchanged).
flat graph is unchanged).
**Blast radius.**
@@ -475,7 +475,7 @@ render/debug surface only, never read by bootstrap or the run loop (C23).
deterministic (C14). Unbound nodes' model bytes are unchanged.
3. The bound slot remains absent from the tunable surface (`params()` /
`param_space` / sweep axes) — `bind`'s tuning-side behaviour is unchanged
(C12/C19), and the compilat is unchanged (C23: render/debug symbol only).
(C12/C19), and the flat graph is unchanged (C23: render/debug symbol only).
4. New unit tests pin the recorded original position and the conditional model
field; a headless guard pins the dimmed `name=value` render and slot-order
faithfulness (including a non-trailing bind). All existing goldens, viewer
@@ -9,7 +9,7 @@
Add an additive, fluent `GraphBuilder` that authors a blueprint's topology by
**typed node handles** and **port names** instead of raw positional indices, and
resolves them to the existing index-wired `Composite` at a single fallible
terminal `build()`. The compilat stays wired by raw index (C23): name resolution
terminal `build()`. The flat graph stays wired by raw index (C23): name resolution
happens at the authoring boundary, the same posture param-name resolution already
has (`Binder`/`with``bootstrap`, `crates/aura-engine/src/blueprint.rs:259,413`).
@@ -66,7 +66,7 @@ mistyped; only port/field names are strings, and they are resolved at `build()`.
From `build()` onward the pipeline is byte-for-byte today's: `validate_wiring`
`lower_items`/`inline_composite``rewrite_edge`/`resolve_target``FlatGraph`.
The compilat never sees a name; the run loop (C1/C2) is untouched. Kind checks
The flat graph never sees a name; the run loop (C1/C2) is untouched. Kind checks
are **not** duplicated in the builder — a resolved edge that connects mismatched
kinds still surfaces through the existing `validate_wiring` / bootstrap kind-check
(name resolution is necessary, not sufficient; kinds remain the structural gate).
@@ -270,7 +270,7 @@ representation is byte-identical to a hand-written `Composite::new`.
BlueprintNode`, addressed by derived role/output names.
- [ ] The raw-index `Composite::new` stays public and unchanged; all existing call
sites compile and pass; both authoring forms interoperate.
- [ ] The compilat is unchanged: no name reaches `FlatGraph`; `compile_with_params`
- [ ] The flat graph is unchanged: no name reaches `FlatGraph`; `compile_with_params`
/ `inline_composite` / `rewrite_edge` / `Harness::bootstrap` / the run loop
are untouched. Determinism (C1) and no-look-ahead (C2) hold by construction.
- [ ] `cargo build --workspace`, `cargo test --workspace`, and
+5 -5
View File
@@ -23,7 +23,7 @@ run. Today an unwired input slot is bootstrapped with an empty column
forgotten connection runs a deterministic but wrong backtest, and two producers
into one slot is ill-formed (a slot holds exactly one column) yet uncompiled-
against. A single new structural check at the existing compile boundary closes
both, for both authoring surfaces, without touching the compilat (C23).
both, for both authoring surfaces, without touching the flat graph (C23).
Non-goal (explicitly out of scope, dropped this cycle): promoting port/field/node
names to load-bearing wiring keys; node-name or port/field-name uniqueness;
@@ -216,7 +216,7 @@ pub enum CompileError {
> concept**: every declared input port is required (no shipped node runs
> meaningfully without one; an unwarmed mode-A input is *wired-but-not-yet-valued*,
> not unwired). The check is **index-based and name-free** — it touches no name
> machinery and emits nothing into the compilat, so C23 is untouched (it proves the
> machinery and emits nothing into the flat graph, so C23 is untouched (it proves the
> existing raw-index wiring is total and single-valued). Inherited identically by
> the raw `Composite::new` path and the `GraphBuilder::build()` path (both compile
> via `compile_with_params`).
@@ -227,7 +227,7 @@ pub enum CompileError {
→ [existing index/kind checks] → `check_ports_connected(nodes, edges, roles)`
[recurse per nested composite] → (on Ok) `UnboundRootRole` check → `lower_items`
`FlatGraph`. The new check is a pure read over the same three slices
`validate_wiring` already holds; it adds no data to the compilat and no work to the
`validate_wiring` already holds; it adds no data to the flat graph and no work to the
run loop.
## Error handling
@@ -260,7 +260,7 @@ need all faults are out of scope (the existing variants are first-fault too).
(`crates/aura-engine/src/test_fixtures.rs`) and the builder harness
(`builder_harness_compiles_identically_to_hand_wired`) still compile green; the
aura-cli sample / sweep / param_space / render tests stay green.
6. **C23 regression — compilat byte-identical.** For any graph that still compiles,
6. **C23 regression — flat graph byte-identical.** For any graph that still compiles,
the lowered `FlatGraph` is unchanged (the existing identity test covers this; the
check adds nothing to lowering).
@@ -329,7 +329,7 @@ slot is covered by its role target, and `UnboundRootRole` is checked after
asserting their original deep-fault variant; and the param-order test
(`param_space_mirrors_compiled_flat_node_param_order_under_nesting`) fully wired and
still asserting its `param_space == compiled-flat-param-order` equality.
- The full existing suite stays green (regression 5) and the compilat is
- The full existing suite stays green (regression 5) and the flat graph is
byte-identical for graphs that still compile (regression 6).
- The C8 ledger realization note is landed.
- `cargo test --workspace` green; `cargo clippy --workspace --all-targets -D