8688a60ded
The single-file ledger had grown to 2968 lines / ~42k tokens, mixing current design law with accreted history: 59 cycle-stamped realization blocks, 18 [HISTORY] passages, 22 supersession markers, and the C10 / C22 / C24 reframe sagas layered several supersessions deep. A code-grounding audit (31 agents, adversarially verified) confirmed 11 defects stated as current truth: stale crate homes from the C28 #288 roster split (cost nodes, PositionManagement, PositionEvent, Session), the renamed InputSpec->PortSpec, the pre-#241 project model in C16 and the open-threads section, a stale HarnessKind retirement deferral in C24, and three C28-internal inconsistencies. New shape, per the ailang precedent: - INDEX.md stays the sole addressable entry point: foundation, external components, a C-id-keyed contract map (one line per contract), and only the genuinely open architectural threads. - contracts/cNN-<slug>.md carries each contract's current truth only: Guarantee / Forbids / Why with ratified refinements integrated, plus a code-anchored Current state. All confirmed defects are fixed here; crate anchors were re-verified against the tree. - contracts/cNN-<slug>.history.md (18 sidecars) and INDEX.history.md preserve every superseded block verbatim, stamps and issue refs intact, under a frozen-record banner. Nothing was deleted: superseded design intent remains an addressable working-tree artifact, off the per-cycle audit walk. - Ledger discipline is now stated in INDEX.md: live files are edited in place at cycle close, superseded text moves verbatim to the sidecar, and a supersession marker in a live file is itself an audit finding. Every contract file was verified against its old text by an independent zero-loss pass (statement-by-statement) plus a code-accuracy spot check; C-ids and contract titles are unchanged, so existing C-id citations in code, tests, and issues resolve as before.
89 lines
6.0 KiB
Markdown
89 lines
6.0 KiB
Markdown
# C9 — Fractal, acyclic composition
|
|
|
|
**Guarantee.** A composite is an **authoring-level** `Node`: it declares the same
|
|
interface (typed inputs + ≤1 output, C8), is wireable wherever a node is, wires an
|
|
interior sub-graph, and exposes one output **port** carrying a **record of 1..K
|
|
re-exported fields** — signal, combined signal, and (with execution) strategy are
|
|
the same abstraction, nestable arbitrarily. A composite is **not a runtime
|
|
object**: the bootstrap compiles it away by **inlining** its sub-graph into the one
|
|
flat instance (C19/C23), so "nestable arbitrarily" and "graph-as-data" hold at the
|
|
**blueprint (source)** level while the running graph is the flat, index-wired
|
|
`FlatGraph`. The dataflow graph is a DAG; the only feedback path is an explicit
|
|
delay/state node (the RTL "register", C1). Wiring is written in Rust (builder API,
|
|
C17); the built graph is introspectable runtime data and, per C24,
|
|
**bidirectional** — a blueprint is not only *emitted* as data but is itself a
|
|
serializable data value with a load path (data → blueprint → `FlatGraph`), so
|
|
topology is a value the World generates, stores, and reproduces. The Rust builder
|
|
stays the primary human/LLM authoring surface; the data form is the machine surface
|
|
the World owns.
|
|
|
|
**Forbids.** Implicit dataflow cycles (combinational loops); special-casing
|
|
"signal-of-signals" as separate mechanics; a composite surviving as a runtime
|
|
sub-engine (a `Box<dyn Node>` driving a nested sub-loop); a blueprint whose
|
|
`param_space()` name projection is non-injective — a path-qualified knob name that
|
|
repeats, since it then has no distinct by-name address (C12/C19).
|
|
|
|
**Why.** Self-application of one contract gives unlimited composition with no
|
|
adapter zoo. Acyclicity keeps the synchronous reactive model well-defined; forcing
|
|
feedback through a visible delay node keeps per-cycle determinism intact (C1) and
|
|
the one legitimate feedback path explicit. Graph-as-data enables visualization,
|
|
freezing, and re-parameterization for sweeps, and — round-tripping both ways (C24)
|
|
— lets the World own topology as content it serializes, loads, and generates rather
|
|
than as Rust baked into the binary. Inlining, not a runtime sub-object, is what
|
|
makes the composite boundary free: it keeps the interior transparent to the
|
|
cross-graph optimiser (C23) and adds no runtime sub-loop the flat model does not
|
|
need; the boundary dissolves into the raw index wiring the run loop already
|
|
consumes, and names stay non-load-bearing (C23).
|
|
|
|
## Current state
|
|
|
|
A composite is `Composite` (`aura-engine::blueprint`), a reusable sub-graph fragment
|
|
that is never `eval`'d. It holds interior items, interior edges (local indices),
|
|
`input_roles: Vec<Role>`, and `output: Vec<OutField>`.
|
|
|
|
- **Multi-output record.** `output` is `Vec<OutField { node, field, name }>` — each
|
|
entry a **named projection** of one interior `(node, output-field)`. A consumer
|
|
selects which re-exported field it reads via `Edge::from_field`, the same binding
|
|
that already selects a leaf record's columns (C8). This is the same arity C8
|
|
grants a leaf (OHLCV = one port, 5 columns): a multi-line indicator (MACD =
|
|
macd/signal/histogram) is one record of K fields, one port, one row per `eval`. A
|
|
strategy composite is the K=1 case (one bias field, C10).
|
|
- **Named boundary.** `input_roles` is `Vec<Role { name, targets }>` — role `r`
|
|
fans its source value into the interior `targets`. Inputs and outputs are ordered,
|
|
positionally-indexed named projections of interior handles.
|
|
- **Per-node param path.** A node's surface param name flows from its own **instance
|
|
name**: every node carries a name (default = its lowercased type label, override
|
|
via `Node::named`, `aura-core::node`), and `param_space()`
|
|
(`aura-engine::blueprint`) is uniformly `<node>.<param>` at every level including
|
|
the root. A same-type fan-in is distinguished by naming the colliding legs — the
|
|
same single act that qualifies their param paths.
|
|
- **Param-namespace injectivity.** A blueprint compiles only if its `param_space()`
|
|
name projection is injective. A duplicated path is a knob no binding can select
|
|
alone (C12/C19), rejected as `CompileError::DuplicateParamPath` carrying the
|
|
offending path; the cure is `.named(...)` on the colliding same-type siblings. One
|
|
structural check, `check_param_namespace_injective` (`aura-engine::blueprint`),
|
|
runs before name resolution in `compile_with_params` and both binders. Paramless
|
|
interchangeable same-name sources stay legal (no path, no duplicate).
|
|
- **Names dropped at lowering.** Output, role, and node names are non-load-bearing
|
|
(C23): they live at the blueprint boundary and in render, and are dropped at
|
|
inline — `ItemLowering::Composite` carries `output: Vec<(usize, usize)>` (raw index
|
|
pairs) and `roles: Vec<Vec<Target>>`, so the flat graph is name-free and wired by
|
|
raw index.
|
|
- **Bidirectional data form.** The emit half is `model_to_json`
|
|
(`aura-engine::graph_model`); the load path is realised —
|
|
`aura-engine::blueprint_from_json` (`aura-engine::blueprint_serde`, #155) parses
|
|
the data form back to a blueprint that compiles to a `FlatGraph` (C24).
|
|
|
|
## See also
|
|
- [C1](c01-determinism.md) — determinism, acyclic dataflow, the delay/state register.
|
|
- [C8](c08-node-contract.md) — the node contract: ≤1 output, record columns, `Edge::from_field`.
|
|
- [C10](c10-bias-r-cost.md) — the strategy as a K=1 bias composite; the cost-model graph.
|
|
- [C12](c12-atomic-sim-unit.md) — binding by name; why a duplicated knob path is unbindable.
|
|
- [C17](c17-authoring-surface.md) — the Rust builder as the human/LLM authoring surface.
|
|
- [C19](c19-bootstrap.md) — bootstrap: blueprint → instance; construction-phase checks.
|
|
- [C23](c23-graph-compilation.md) — names non-load-bearing; the cross-graph optimiser; inlining.
|
|
- [C24](c24-blueprint-data.md) — the blueprint as a serializable, World-owned data value.
|
|
- [C28](c28-stratification.md) — the layer ladder: construction phase vs the run loop.
|
|
|
|
> History: [c09-fractal-composition.history.md](c09-fractal-composition.history.md)
|