Phase 4 of the Stratification milestone. aura-std held four C28 ladder
layers in one roster; this cuts them into layer-aligned, aura-core-only
node crates so the import direction is enforced by the crate graph:
- aura-std — engine nodes only (arithmetic/logic/rolling + sinks)
- aura-market — session, resample
- aura-strategy — bias, stops, sizer, cost-model machinery
- aura-backtest — sim_broker, position_management
- aura-vocabulary — the relocated closed std_vocabulary roster
Node modules move verbatim (byte-identical renames); consumers are
rewired by import path only. A new structural test
(aura-vocabulary/tests/c28_layering.rs) asserts each node crate's
[dependencies] stay within its C28-permitted inner set, catching the
acyclic-but-outward violation the compiler misses.
Behaviour byte-identical: full workspace suite green (1448 tests), no
golden edited, clippy -D warnings clean. C28 Status block updated.
closes#288
First slice of declared taps: a Tap { name, from: TapWire { node, field } }
is the output-side twin of input_roles — a pure data declaration naming an
interior output wire, no sink endpoint. Composite gains a taps field
(with_taps/taps(), additive) and the serde mirror CompositeData carries it
(Tier-1 additive: no format-version bump, absent-taps documents byte-stable).
Tap/TapWire are re-exported from the crate root beside Role/OutField so
downstream crates can author tap-bearing blueprints.
refs #282
The blueprint data format (C24) extends additively under one codified
two-tier discipline:
- Tier-1 (additive-optional): a new optional field/section is tolerated by an
older reader (serde ignores unknown fields — no deny_unknown_fields), with no
format_version bump; by C1 a new optional field defaults to prior behaviour,
so an old blueprint reproduces the same graph.
- Tier-2 (must-understand: a new node type, edge semantics, or structural-axis
kind): bumps format_version, so an old reader refuses cleanly
(LoadError::UnsupportedVersion / UnknownNodeType) rather than silently
building a different graph.
Most of #156 already shipped with #155 (cycle 0087): the envelope, omit-defaults
canonical form, and both Tier-2 load-path refusals were present and green. This
cycle pins the one previously-unproven property — Tier-1 forward-tolerance of an
unknown optional field on the current loader — and codifies the discipline.
Changes:
- New characterization pin (in-crate, blueprint_serde.rs):
unknown_optional_field_is_tolerated_byte_identically — an unknown optional key
at doc and primitive level loads byte-identically and runs bit-identically.
- Two public-seam E2E pins (blueprint_serde_e2e.rs, added by the E2E phase):
unknown_envelope_field_tolerated_through_public_api and
unknown_primitive_field_tolerated_through_public_api — split the two distinct
tolerance surfaces (envelope vs primitive) across the World/cdylib crate
boundary, where a deny_unknown_fields added to one would break forward-compat
while leaving the in-crate test green.
- LoadError::UnsupportedVersion doc reworded from the deferred "is #156" note to
the settled two-tier discipline statement.
- C24 ledger (docs/design/INDEX.md): Status codification sentence added; #156
dropped from Remaining (#158/#159 stay).
Derived fork (recorded on #156): the per-section required-flag mechanism (PNG
critical-vs-ancillary) is NOT built — the version envelope already gives the
must-understand refusal at version granularity, no current Tier-2 section
validates a finer scheme, and C1 holds either way.
The pin is a characterization test (GREEN on first run) — it documents existing
serde silent-ignore behaviour, no production code changed. Verified: cargo test
-p aura-engine green (all targets, three new tests confirmed by name); cargo
clippy --workspace --all-targets -- -D warnings clean.
C24 first cut (#155): a blueprint — the param-generic, named graph-as-data a
Rust builder produces — is now a first-class serializable data value with a
canonical, versioned format, and the engine has the missing load path
(data -> blueprint -> FlatGraph), not only the Rust-builder construction path.
What ships:
- `blueprint_to_json` / `blueprint_from_json` in a new `aura-engine`
blueprint_serde module: a faithful serde DTO projection (the build closures
dropped; re-derived on load), top-level `format_version` envelope, canonical
compact JSON (struct field order, defaults omitted via skip_serializing_if).
- A resolver seam: the loader is generic over an injected
`Fn(&str) -> Option<PrimitiveBuilder>`; the concrete closed `match` over the
aura-std vocabulary lives in aura-std (`std_vocabulary`), never in the engine
(the engine stays domain-free: lib deps aura-core + aura-analysis only, never
the node-vocabulary crate). This is C24's compiled-in closed-set referenced by
type identity, NOT a dynamic/marketplace node registry (domain invariant 9).
- serde derives on the serialized plain types (Edge, Target, OutField, Role,
BoundParam, ScalarKind); BoundParam additionally re-exported from aura-core's
root (an additive fix the plan's file list missed).
Load-bearing scope decisions (derived, logged on #155):
- Sinks are excluded from the serialized blueprint — a recording sink captures an
mpsc::Sender (runtime identity, not param/topology, C19 value-empty); sink
addressing is the C18-registry / #101 concern. The round-trip test attaches
identical sinks post-load to both twins.
- Construction-arg builders (LinComb(arity), CostSum(n), SimBroker(pip),
Session(..)) are out of the round-trippable set: their structural args are a
C20 structural-axis concern the param-generic format does not yet encode; an
absent type_id fails the load cleanly (UnknownNodeType), never a silent wrong
graph. #155's vocabulary is the 22 zero-arg aura-std builders. Additively
extensible later (#156).
Orchestrator hardening on review: the serializer now emits bound params in
ascending original-slot order (mirroring the loader), so the canonical form is
bind-order-independent — a precondition for content-addressing (#158). Identity
today (every #155 node has <=1 param); holds by construction for future
multi-param nodes.
Acceptance (all green): a serialized blueprint runs bit-identical (C1) to its
Rust-built twin; serializer/loader are inverse (canonical idempotence, incl. a
recursive nested composite); unknown-type and unsupported-version fail named,
never panicking. cargo test --workspace 748 passed; clippy --all-targets
-D warnings clean.
closes#155