main
977 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
227d004c9d |
feat(aura-engine): reject unwired or double-wired input ports
A graph that leaves an interior input slot unconnected, or wires one slot from more than one producer, is now a compile-time error instead of a silently-wrong run. Until now an unwired interior slot was accepted and bootstrapped to an empty column (harness.rs:137-148), so a forgotten connection ran a deterministic but wrong backtest; two producers into one slot — ill-formed, since a slot holds one column — was likewise uncompiled-against. A single name-free check, `check_ports_connected`, is added to `validate_wiring` after the existing index/kind checks and before the nested-composite recursion. It counts coverage of each (interior-node, slot) uniformly across interior edges and role targets, and rejects zero coverage (new `CompileError::UnconnectedPort`) or >1 (new `CompileError::DoubleWiredPort`). Because `validate_wiring` is called once from `compile_with_params` and recurses, both the raw `Composite::new` path and the `GraphBuilder::build()` path inherit it at every nesting level with no builder-side code (mirrors `check_param_namespace_injective`, the param-side sibling). A composite's own open input roles (source: None) are coverage providers — the wired-by-enclosing boundary, the root already guarded by UnboundRootRole — not consumers. Index-based and name-free: nothing reaches the compilat, so C23 holds. Supporting fix: the check computes `signature()` on every interior node before the recursion validates that node's interior, so `derive_signature` and `interior_slot_kind` are made bounds-total (a placeholder kind for an out-of-range OutField/target, never a panic; the real fault is still reported by validate_wiring's guarded OutputPortOutOfRange/BadInteriorIndex). This latent panic was exposed by the new check, not introduced by it. Test maintenance (blast radius enumerated empirically, not hand-traced): four existing tests relied incidentally on under-wiring being accepted — three negative tests get a covering root role so their intended deep fault still surfaces via the recursion, and one param-order nesting test is fully wired (param order unchanged). Six new tests: five raw-surface (unwired, double-wire via edge+role and edge+edge, nested, open-role boundary) and one builder-surface forgotten-leg (proving the GraphBuilder inherits the check). Narrowed scope of #65: the original "promote names to load-bearing wiring keys / close the exposure-price swap structurally" goal was dropped — #64 already moved authoring to handles + visible port names, so the residual same-kind swap is a valid-but-wrong name choice no structural check catches, and a structural fix would push domain semantics into the domain-free engine (C10/C7). This ships the name-free half that stands on its own: wiring completeness, which catches forgotten and doubled connections. Verified: cargo test --workspace 231 passed / 0 failed; cargo clippy --workspace --all-targets -- -D warnings clean. closes #65 |
||
|
|
277f8714d4 |
plan: 0040 add derive_signature bounds-guard step
Implement-time discovery: the new check (and the existing edge/role checks) compute signature() on every interior node before the recursion validates that node's interior. For a structurally-invalid composite (an out-of-range OutField or role target), derive_signature indexed unguarded and PANICKED — exposed by output_port_out_of_range_rejected the moment its covering root role forces c.signature(). Added Task 1 Step 2b: make derive_signature and interior_slot_kind bounds-total (a placeholder kind for an invalid index; the real fault is still reported by validate_wiring's guarded OutputPortOutOfRange / BadInteriorIndex). Fix verified empirically before re-dispatch. Not a design change to the check itself. refs #65 |
||
|
|
669541c6f6 |
plan: 0040 wiring-totality-check
Seven-task plan for the wiring-totality check (parent spec docs/specs/0040-wiring-totality-check.md, |
||
|
|
69b16f7d39 |
spec: 0040 correct blast radius to four (empirically probed)
The boss-signed 0040 spec's blast-radius section claimed "exactly three" flipping tests, named from a hand-trace. A throwaway probe (the check implemented, full workspace suite run, then discarded) found FOUR: the three named negative tests plus `param_space_mirrors_compiled_flat_node_param_order_under_nesting`, a compile-success param-order test that today compiles a deliberately under-wired nested graph (LinComb's two term inputs never wired) just to read its flat param order. The new check rejects it where it currently returns Ok. Correction is editorial, not a design change: the check itself is unchanged. The fourth test's fix differs from the three negative tests — it needs the contrived graph fully wired (fan fast_slow's output into both LinComb terms + a covering root role), not just a covering role — so the §Blast radius and §Acceptance criteria now enumerate all four with their distinct fixes. Verified empirically (119 passed, 4 failed; every other crate green), the strongest possible grounding for the count. refs #65 |
||
|
|
07b1ae1c63 |
spec: 0040 wiring-totality-check (boss-signed)
Reject graphs with an unwired or double-wired interior input port. A new
name-free structural check `check_ports_connected` in `validate_wiring`
(crates/aura-engine/src/blueprint.rs) requires every interior node's every
declared input slot to be covered by exactly one wiring act — one `Edge{to,slot}`
or one `Role` `Target{node,slot}`, counted uniformly. Zero coverage is the new
`CompileError::UnconnectedPort` (a forgotten connection that today bootstraps a
silent empty column, harness.rs:137-148); more than one is `DoubleWiredPort` (a
slot holds one column). Run once at the existing compile boundary and recursive
per nesting level, so both the raw `Composite::new` and the `GraphBuilder::build`
surfaces inherit it. Index-based, name-free — the compilat is untouched (C23).
Narrowed scope of #65: the original "promote names to load-bearing wiring keys /
close the exposure-price swap structurally" goal is dropped (see the reconciliation
comment on the issue) — #64 already moved authoring to handles + visible port
names, so the residual same-kind swap is a valid-but-wrong name choice no
structural check catches, and a structural fix would push domain semantics into
the domain-free engine (C10/C7). This cycle ships only the name-free half that
stands on its own.
Auto-signed under /boss: precondition gate clean (no fork silently picked),
self-review + parse-trace clean (no spec-validation parser declared -> documented
no-op), grounding-check PASS on the final bytes (one BLOCK on an incomplete
blast-radius survey, repaired forward by naming the three flipping negative tests),
and a unanimous five-lens spec-skeptic panel (criterion, grounding, scope-fork,
ambiguity, plan-readiness all SOUND).
refs #65
|
||
|
|
b6174cff80 |
refactor(aura-engine): rename NodeHandle::in_/out to input/output
The trailing underscore on in_() was a keyword-escape scar (`in` is reserved).
Rename the NodeHandle port/field accessors to input()/output(), which also
aligns the authoring surface with the schema's own nomenclature
(NodeSchema.inputs/output, "input port"/"output field"): node.input("lhs")
now reads in the same vocabulary the schema uses, instead of a second one.
Mechanical, behaviour-preserving: the two method definitions plus every call
site in builder.rs and the aura-cli sample blueprints. Full workspace green;
clippy --all-targets -D warnings clean.
|
||
|
|
50105c1957 |
refactor(aura-cli): author the sample blueprints via GraphBuilder
Adopt the name-based GraphBuilder (cycle 0039) in the CLI sample blueprints, which previously hand-wired raw positional Edge/Role/OutField via Composite::new. sma_cross, macd, signals, sample_blueprint_with_sinks, and macd_strategy_blueprint now read as typed-handle authoring: nodes are added for NodeHandles, ports/fields are wired by name (series/lhs/rhs/value, exposure/price, term[i], col[0], the macd histogram/signal/macd outputs), and build() lowers to the same index-wired Composite. This is what #64 was for — the builder was previously exercised only in its own tests; the showcase sample now dogfoods it. Behaviour-preserving: node order, instance names (.named), bound params (blend.weights[2]), and wiring are byte-identical, so param_space, the graph render, the single run, and the sweep are unchanged — guarded green by the existing aura-cli sample/sweep/param_space/render tests and the engine suite (full workspace 0 failed; clippy --all-targets -D warnings clean). sample_harness is deliberately left as a direct FlatGraph construction (baked Sma::new(2) nodes, the single-run path) — it builds the compilat directly, not a value-empty Composite, so it is not a GraphBuilder target. |
||
|
|
7865030d33 |
audit: cycle 0039 — drift-clean (GraphBuilder name-based wiring)
Close-audit for cycle 0039 (commit range 3a1dceb..a6a314b). Architect drift review against the design ledger + CLAUDE.md domain invariants: no contract drift, no debt. No regression scripts are configured (the architect is the gate); build + test + clippy verified green independently by the orchestrator (aura-engine 123 passed, +9 new; full workspace green; clippy --all-targets -D warnings exit 0). cycle 0039 (clean): - C23 / C11 preserved: the compilat is byte-unchanged this cycle. harness.rs (Edge / FlatGraph / Target / SourceSpec) is untouched; the new GraphBuilder resolves every port/field name in build() (resolve_slot / resolve_field) and lowers to the unchanged Composite::new with raw-index Edge / Role / OutField. No name-carrying field was added to any compilat type — names resolve at the authoring boundary and never reach FlatGraph. - C10 / C17 preserved: GraphBuilder is a genuine fluent Rust builder over the existing structs, not a DSL. Node references are typed Copy handles; only port/field names are strings, resolved against declared NodeSchema data. - C8 / C1 / C2 preserved: resolution is cold authoring-time; the run loop and the bootstrap kind-check remain the structural gate (name resolution is necessary, not sufficient). - #21 deferral is coherent: the exposure/price swap is made legible (named slots) but deliberately NOT structurally closed; issue #65 carries the structural follow-up (promoting port/field names to load-bearing keys), and no ledger contract claims the swap is closed. Awareness (not drift, spec-consistent): feed() indexes self.roles[role.0] directly, so a RoleHandle minted by a different builder panics rather than returning BuildError::BadHandle (which covers only NodeHandles, per the signed spec). RoleHandle out-of-range robustness is unspecified by spec 0039. |
||
|
|
a6a314bb98 |
feat(aura-engine): name-based blueprint wiring via GraphBuilder
Add an additive, fluent GraphBuilder authoring surface that wires a blueprint's topology by typed node handles and port/field names instead of raw positional indices. Node references are Copy NodeHandle values (a node reference cannot be mistyped); only port/field names are strings, resolved against each node's cached NodeSchema by exactly-one-match at a single fallible build() -> Result<Composite, BuildError> — the Binder posture, one level up. build() lowers to the unchanged Composite::new, so the compilat stays index-wired (C23): names resolve at the authoring boundary and never reach FlatGraph. A new From<Composite> for BlueprintNode lets add() accept nested composites. Verified: builder-authored sma_cross is structurally equal to the hand-wired fixture, and the full builder-authored harness lowers to a byte-identical FlatGraph (equal edges + sources). The five BuildError variants (Unknown/Ambiguous In/Out, BadHandle) are covered, and the SimBroker exposure/price legs are now addressable by name (the #21 legibility win). The structural close of the exposure/price swap (#21) is deliberately out of scope — this builder makes the swap legible, not impossible — tracked as #65. Two plan-test corrections applied during implementation (both in-scope, no behaviour change): the harness parity test uses compile_with_params with a topology-invariant param vector on both sides (the harness declares three params, so a no-param compile() would trip ParamArity), and the error asserts use .err()/Some(...) rather than unwrap_err() (Composite is not Debug). aura-engine 123 tests green (+9); full workspace green; clippy --all-targets -D warnings clean. Existing index-form Composite::new sites and tests untouched (coexistence). closes #64 |
||
|
|
031081bf62 |
plan: 0039 graphbuilder-name-based-wiring
Task-by-task plan for the typed-handle GraphBuilder: the From<Composite> lift, the builder module (types + accumulators + the fallible build() resolver), the lib.rs wiring, and the test suite (structural parity, harness FlatGraph parity, the five BuildError variants, #21 legibility, coexistence + clippy gate). refs #64 |
||
|
|
292c95756f |
spec: 0039 graphbuilder-name-based-wiring (boss-signed)
Add a typed-handle GraphBuilder authoring surface for blueprint topology: node references become Copy NodeHandle values, ports/fields resolve by name against the existing PortSpec.name/FieldSpec.name at a single fallible build() terminal, lowering to the unchanged index-wired Composite (C23 holds by construction — names never reach the compilat, mirroring param-name resolution). Spec auto-signed under /boss: all objective gates green (precondition, self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel (criterion, grounding, scope-fork, ambiguity, plan-readiness all SOUND). The structural close of the SimBroker exposure/price swap (#21) is explicitly out of scope, tracked as #65. refs #64 |
||
|
|
3a1dceb83a |
audit: cycle 0038 — drift-clean (shared SMA-cross test fixtures)
Close-audit for cycle 0038 (commit range c939673..1f01bad). Architect drift review against the design ledger + CLAUDE.md domain invariants: no contract drift, no debt. No regression scripts are configured (the architect is the gate); build + test + clippy verified green independently by the orchestrator (aura-engine 114 passed, count unchanged; the two load-bearing consumers green; clippy --all-targets -D warnings exit 0). cycle 0038 (clean): - C16 preserved: the new shared fixtures live in a #[cfg(test)] mod test_fixtures, which compiles into no shipped artifact — the engine still ships no sample. - C1 / C11 / C12 strengthened, not weakened: the determinism witness (composite_sma_cross_runs_bit_identical_to_hand_wired) and the sweep-equivalence witness (sweep_equals_n_independent_runs) now consume one shared fixture instead of two hand-kept copies, so the harness topology can no longer silently diverge between the two test modules — the exact hazard #53 named. - Test-organisation convention held: a crate-root #[cfg(test)] module of pub(crate) free fns, sibling to the existing reexport_tests precedent, declared in lib.rs. - graph_model.rs's minimal golden copy (sma_cross / sample_root) was correctly left independent and out of scope, by design. - Stale-cross-ref sweep clean: docs/design/INDEX.md and docs/glossary.md name no fixture site that the move invalidated; the fixture-name hits in docs/ are historical specs/plans pinned to their own cycles, not live contracts. In-flight plan correction (recorded for history): the plan's literal import list over-imported sma_cross, which neither consumer calls directly (reached only transitively via composite_sma_cross_harness); the implementer trimmed it to satisfy the plan's own clippy -D warnings gate — within the task's import-cleanup scope. |
||
|
|
1f01badac7 |
refactor(aura-engine): share the SMA-cross harness test fixtures
Collapse the three verbatim-duplicated #[cfg(test)] fixtures — synthetic_prices, sma_cross, composite_sma_cross_harness — that lived in both blueprint.rs's and sweep.rs's test modules into one shared crate-root `#[cfg(test)] mod test_fixtures` (pub(crate) free fns). The two consumers now import from `crate::test_fixtures`; sweep.rs's test-mod `use` block sheds the imports that were only needed by the moved fns. Why: the topology had to move in lockstep across the two copies but no test pinned them equal, so an edit to one silently diverged the other (#53). One definition removes the hazard structurally — better than a pin-them-equal test, which loses its premise once there is a single definition. Test-only; no production code, public API, or runtime path changes. C16 untouched (a #[cfg(test)] module ships in no artifact). The CLI sample is no longer a third peer (cycle 0036 enriched it into a trend+momentum blend), so this is an engine-internal two-copy dedup, not the three-way the issue originally named. graph_model.rs keeps its own intentionally-minimal golden copy, out of scope. Plan glitch fixed in-flight: the plan's literal import list named sma_cross, but neither consumer calls it directly (only transitively via composite_sma_cross_harness), so importing it tripped the plan's own Step-7 clippy -D warnings gate. Trimmed to {composite_sma_cross_harness, synthetic_prices} — within the task's "drop now-unused imports" scope. Verified: cargo test --workspace green (aura-engine 114 passed, count unchanged — behaviour-preserving); the two load-bearing fixture consumers composite_sma_cross_runs_bit_identical_to_hand_wired (C1) and sweep_equals_n_independent_runs (C11/C12) both green; cargo clippy --workspace --all-targets -- -D warnings clean. closes #53 |
||
|
|
1718ba0f98 | plan: 0038 dedup-sma-cross-test-fixtures | ||
|
|
47179ce5a4 |
spec: 0038 dedup-sma-cross-test-fixtures (boss-signed)
Collapse the three verbatim-duplicated SMA-cross harness test fixtures (synthetic_prices, sma_cross, composite_sma_cross_harness) in aura-engine's blueprint.rs and sweep.rs #[cfg(test)] modules into one shared crate-root #[cfg(test)] mod test_fixtures. Behaviour-preserving, test-only. Auto-signed under /boss: all objective gates green (precondition, self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel returned SOUND. refs #53 |
||
|
|
c939673aa7 |
audit: cycle 0037 — drift-clean (bound params in the graph model + viewer)
Close-audit for cycle 0037 (commit range d069484..47e0e60). Architect drift review
against the design ledger + CLAUDE.md domain invariants: no contract drift, no debt.
No regression scripts are configured (the architect is the gate); build + test +
clippy verified green independently by the orchestrator.
cycle 0037 (clean):
- C23 preserved: bind still resolves the slot name to a fixed position and re-splices
the value in the build closure; BoundParam/"bound" is recorded purely beside that
capture, a render/debug symbol dropped at lowering — bootstrap and the run loop
never read bound_params() (only the model serializer does). The compilat stays
index-wired. A genuine twin of the cycle-0035 instance-name thread.
- C14 preserved: the inline no-bind model_golden (sample_root) is byte-unchanged;
the "bound" field is conditional (mirrors "name"), so unbound nodes are
byte-identical, and only the sample fixture's blend node gained the field.
scalar_str is total over all four Scalar variants and deterministic (f64 via {:?}
keeps a decimal point so a bound f64 never reads as an i64).
- C12/C19 preserved: the tunable surface is untouched — bind still removes the slot
from schema.params; the eight-param sweep pins stay byte-identical with weights[2]
absent. The render annotation reads from a separate bound vec, not param_space().
Test coverage: the original-position unit test (trailing + reverse-chained), the
prim_record conditional-field unit test (i64 + f64), and a headless render guard
pinning slot-order faithfulness for both a trailing and a middle bind (the middle
case catches an append-only regression).
Resolution: carry-on. Next iteration: pick the next backlog item.
|
||
|
|
47e0e605e1 |
feat(aura): surface bind-bound params in the graph model + viewer signature
A node built with `.bind(slot, value)` fixes a declared param to a structural
constant: the slot correctly leaves the tunable surface (param_space / sweep axes),
but it also vanished from the RENDERED signature, because prim_record emitted only
schema.params. The cycle-0036 `blend` (a LinComb(3) with weights[2] bound to 0.5)
rendered `LinComb[weights[0], weights[1]]` — a 2-arity signature over a 3-port box,
with the fixed 0.5 invisible. The signature misrepresented the node.
bind now ANNOTATES instead of erasing. All slots are shown; the bound one renders
`name=value`, dimmed. The blend renders:
blend: LinComb[weights[0], weights[1], weights[2]=0.5]
Structurally a twin of cycle 0035's instance-name thread (commit
|
||
|
|
4227073fce |
plan: 0037 bound-param-in-graph-model
Six bite-sized tasks for spec 0037: aura-core records a BoundParam (original slot position) in bind + a bound_params() accessor; aura-engine emits a conditional "bound" field via scalar_str; graph-viewer.js merges free+bound params into slot order and renders the bound slot dimmed as name=value; re-capture the sample-model fixture; a new headless render guard (trailing + middle bind); full-workspace gate. refs #63 |
||
|
|
42850c6f11 |
spec: 0037 bound-param-in-graph-model (boss-signed)
Surface a bind-bound param in the graph model and the `aura graph` viewer signature instead of dropping it. A node built with `.bind(slot, value)` keeps the slot off the tunable surface (param_space / sweep axes — unchanged, correct) but now ANNOTATES it in the render: all slots shown, the bound one as `name=value`, dimmed. The cycle-0036 `blend` renders `LinComb[weights[0], weights[1], weights[2]=0.5]`. Structurally a twin of cycle 0035's instance-name thread: a conditional field (here `"bound"`) threaded engine -> model -> viewer, plus goldens and a headless render guard. Render/debug surface only — dropped at lowering (C23), model stays deterministic (C14). Auto-signed under the /boss spec auto-sign gate: precondition clean (the one open notation decision was settled with the user in-session and recorded as a provenance-bearing reconciliation comment on the issue), self-review + parse-trace clean, grounding-check PASS, and a unanimous five-lens spec-skeptic panel. refs #63 |
||
|
|
d069484559 |
audit: cycle 0036 — refresh stale sample cross-refs; drift-clean otherwise
Close-audit for cycle 0036 (commit range 560c2d0..94af4c7). Architect drift review against the design ledger + CLAUDE.md: no contract drift. No regression scripts are configured (the architect is the gate); build + test + clippy verified green. cycle 0036 tidy (clean): - C8/C9/C10 preserved: the enriched sample is multiply-nested composites over shipped primitives — multi-output is the macd composite re-exporting columns (not a >1-record primitive); brokers stay downstream nodes. - C12/C19 preserved: the eight sweep axes are a bijection with the injective, path-qualified param_space under nesting; the bound blend.weights[2] is correctly absent from both the axes and the surface. The re-captured render golden, the two in-crate pins, and the cli_run E2E pin all carry the identical eight-tuple in lockstep. - C14 preserved: the re-captured sample-model.json is the deterministic model of the enriched sample; the engine's own minimal model_golden (sample_root) is independent and byte-unchanged. - C23 preserved: bind resolves the param name to a fixed position; the fixed blend weight is a structural constant (deform-not-tune), dropped from the tuning surface, and the compilat is unchanged. Two stale cross-references found and refreshed (doc debt, no behaviour change): - crates/aura-engine/src/graph_model.rs: sample_root's "Mirrors build_sample() (...:161-190)" was doubly stale — build_sample moved and the CLI sample is now the richer signals graph; re-grounded to state sample_root is a deliberately minimal, independent serializer fixture. - crates/aura-cli/src/main.rs: sample_blueprint_with_sinks' "single source of the sample topology" + "SMA lengths + exposure scale" overclaimed post-enrichment; re-grounded to the root harness whose signal is the nested signals composite, with the eight free params. |
||
|
|
94af4c788c |
feat(aura-cli): enrich the graph sample into a trend+momentum blend showcase
The built-in sample that `aura graph` renders and `aura sweep` runs is now a
"trend + momentum, weighted blend" strategy that exercises four authoring/viewer
capabilities the single-level sample left dark:
- multiply-nested composites: root -> signals -> {trend = sma_cross, momentum = macd};
- a multi-param node inside a composite: blend = LinComb(3) (weights[0]/[1] shown);
- a multi-output node: momentum (the macd composite) re-exports macd/signal/histogram,
two of which the blend consumes;
- bind(): blend.weights[2] is fixed as a structural constant, so it drops out of the
sweepable param surface (and renders absent).
The new `signals` composite reuses the existing sma_cross/macd builders unchanged; the
sweep re-paths its axes to the eight free params (still a 4-point grid) and runs an
18-tick warm-up stream (showcase_prices) so the MACD EMA-of-EMA chain and the all-Any
LinComb join warm up. The render fixture (sample-model.json) is re-captured, and a new
headless guard (viewer_nested_depth) pins that the viewer renders the nesting to depth 2
with valid Graphviz ids. Pure authoring over already-shipped primitives — no engine or
graph-viewer.js change.
The flat run_sample, run_macd/macd(), the inline model_golden test, and the viewer are
untouched. A third sweep-param pin in tests/cli_run.rs (the aura sweep E2E) was re-pathed
in lockstep with its in-crate twin.
closes #62
|
||
|
|
30c1ad5e2e |
plan: 0036 sample-showcase-blueprint
Five tasks for the sample-showcase cycle: (1) the enriched blueprint source — LinComb import, the `signals` composite (trend = sma_cross, momentum = macd, blend = LinComb(3) with weights[2] bound), the node-0 swap in sample_blueprint_with_sinks, the re-pathed sweep_family axes + a showcase_prices warm-up stream; (2) re-path the two in-crate tests (bootstrap-runs-drains, sweep odometer); (3) re-capture the sample-model.json render fixture; (4) a new headless depth-2 nesting guard (viewer_nested_depth.mjs + .rs); (5) the workspace build/test/clippy gate. refs #62 |
||
|
|
e7393f6809 |
spec: 0036 sample-showcase-blueprint (boss-signed)
Enrich the `aura graph` / `aura sweep` sample blueprint into a "trend + momentum, weighted blend" strategy that showcases four authoring/viewer capabilities the single-level sample left dark: multiply-nested composites, a multi-param node inside a composite, a multi-output (MACD-like) node, and bind() (a param fixed as a structural constant, dropped from the sweep surface). Pure authoring over already-shipped primitives — no engine semantics or viewer change; reuses the existing sma_cross/macd builders. Auto-signed under /boss (spec auto-sign enabled): objective gates green (precondition clean, self-review clean, grounding-check PASS) and a unanimous five-lens spec-skeptic panel (criterion, grounding, scope-fork, ambiguity, plan-readiness) returned SOUND. Design provenance recorded on #62. refs #62 |
||
|
|
560c2d0824 |
audit: cycle 0035 — drift-clean (node instance name in the graph model)
Close-audit for cycle 0035 (commit range 20000dd..0ae8320). Architect drift
review against the design ledger + CLAUDE.md: clean. No regression scripts are
configured (the architect is the gate); build + test + clippy verified green.
cycle 0035 tidy (clean):
- C23 preserved: the instance name is resolved to Option at authoring
(instance_name()), emitted only on the render/model surface, and dropped at
lowering — the compilat wiring is byte-unchanged.
- C14 preserved: model bytes stay deterministic; both golden twins (inline
model_golden + sample-model.json) moved in lockstep to the same two "name"
additions.
- C12/C19 untouched: the cycle adds no param-space surface; the param-ganging
alternative was deflected to idea #61 (not via name collision — param_space
is injective by design).
- Engine<->viewer lockstep intact: the conditional "name" producer (prim_record)
and its three consumers (adaptNodes, genDot leaf-emit, cellLabel) moved
together, protected by a prim_record unit test + a headless render guard.
The blueprint-viewer `SMA[length]` (
|
||
|
|
0ae8320d14 |
feat(aura): surface explicit node instance name in the graph viewer
A leaf primitive built with `.named("fast")` now renders its `aura graph` viewer
box head as `fast: SMA[length]` — the instance name as a `:` declaration prefix.
An unnamed leaf keeps the bare `SMA[length]`.
Engine half: a new raw `PrimitiveBuilder::instance_name() -> Option<&str>` (the
explicit name, default not resolved) feeds a conditional leading `"name"` field
in prim_record, present only for an explicitly-named node; both golden twins
(inline model_golden + sample-model.json) re-captured. Viewer half: adaptNodes
carries `name`, the genDot leaf-emit forwards it, and cellLabel prepends the
`name: ` prefix when present (composites stay bare). `named()`'s non-empty
debug_assert is unchanged, its doc re-grounded to the now load-bearing Some/None
invariant (knob-address segment + prefix switch).
The name is a render/model-only debug symbol — dropped at lowering (C23), and the
model stays deterministic (C14). Parameter ganging (one knob, several nodes) was
considered and spun off as an explicit composite-shared-param idea (#61), not via
name collision (param_space is injective, C12/C19).
closes #58
|
||
|
|
77ddbeb237 |
plan: 0035 node-name-in-graph-model
Six tasks for the node-name-in-graph-model cycle: (1) aura-core instance_name() accessor + re-grounded named() doc; (2) aura-engine prim_record conditional "name" field + unit test; (3) re-capture both golden twins (inline model_golden + sample-model.json); (4) viewer render guard (RED, headless node guard); (5) viewer adaptNodes/genDot/cellLabel name prefix (GREEN); (6) workspace build/test/clippy gate. refs #58 |
||
|
|
a421b5a8e1 |
spec: 0035 node-name-in-graph-model (boss-signed)
Surface a node's explicit instance name in the `aura graph` viewer: a named leaf renders its box head as `fast: SMA[length]` (the instance name as a `:` declaration prefix), an unnamed leaf keeps the bare `SMA[length]`. The name crosses two surfaces — a conditional `"name"` field in the JSON graph model (engine half) and a `cellLabel` prefix in graph-viewer.js (viewer half). Explicit-`.named()`-only; `node_name()`'s lowercased-type default is not surfaced. `named()`'s non-empty rule is kept with its code unchanged, its doc re-grounded to the load-bearing invariant (knob-address segment + Some/None prefix switch). Signed via the /boss auto-sign gate: all objective gates green (precondition, self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel (criterion, grounding, scope-fork, ambiguity, plan-readiness). Two earlier blocks were resolved before the unanimous round — a separator-ambiguity by an editorial fix, and a scope-fork (which nodes carry the prefix) by an explicit user decision recorded as a provenance-bearing reconciliation comment on the issue. refs #58 |
||
|
|
a051571d79 |
feat(aura-cli): render leaf param signature as TYPE[...] in the graph viewer
The viewer box head built the param signature in round parens — SMA(length) — which reads as a function call. Switch the brackets to square — SMA[length] — so the head reads as parametrisation, not invocation. cellLabel render-only change in graph-viewer.js; the JSON model surface and both goldens are untouched. |
||
|
|
3c698d4200 |
chore: move skills-plugin profile into CLAUDE.md project facts
The skills plugin dropped dev-cycle-profile.yml. Migrate this project's facts into CLAUDE.md under '## Skills plugin: project facts' and remove the profile file. |
||
|
|
20000ddeb3 |
audit: cycle 0034 (#55) — record structural-constant bind in the ledger; drift-clean otherwise
Architect drift review over 52e0214..HEAD. All hard invariants hold; the only drift was a ledger-prose gap — the new .bind() structural-constant affordance had no contract entry, so the code offered a capability the contracts did not name. Contracts (all hold): - C23: bind resolves a param name -> position at authoring time and stores the index; the compilat stays wired by raw index, the name never reaches it. The by-name authoring address space (0032 amendment) is exactly where bind sits. - C19/C8: the shrink is schema.params.remove(pos) on the single source of truth that params()/schema()/collect_params/lower_items all read, so no desync is structurally possible; param_space() injectivity (0032) is untouched (bind only removes entries). The param-declared-once posture holds. - C9/C10: a pure builder-level value op — no by-name runtime node lookup, no registry, no DSL-style dynamic resolution. - C16: zero Cargo.toml / lockfile changes. - Construction layer (collect_params/lower_items/param_space/compile_with_params) byte-unchanged — verified independently; the blueprint.rs diff is a test-only hunk at 1886, the sole production edit is bind in aura-core/src/node.rs. Regression: none configured (profile regression: []) — no-op; architect is the gate. Resolution -- fix (1 ledger note, added inline this commit): - docs/design/INDEX.md C19 — added a cycle-0034 realization note recording PrimitiveBuilder::bind as the structural-constant path (knob REMOVED from param_space), distinct from the cycle-0016 value-pin (knob stays, fixed in the injected vector); the #55 deform-vs-tune discriminator; C23 unaffected; export of a named frozen strategy deferred to #60. Cycle 0034 drift-clean after this note. Not a milestone close (no milestone fieldtest run). Gates: doc-only edit, compile-inert; cargo build/clippy/test --workspace verified green this session. |
||
|
|
7d587d0c4e |
feat(aura-core): PrimitiveBuilder::bind — fix a node param as a structural constant
Add `PrimitiveBuilder::bind(slot, value)`: bind a declared param to a structural constant, removed from param_space entirely rather than pinned in the injected vector. This closes the gap #55 names — param-bearing nodes (Sma/Exposure/LinComb) had no constant path, only the no-sweep-mode SimBroker precedent. The motivating case is "SMA2-entry": the 2 is bound to the two-candle construction, so sweeping it would enumerate deformed strategies as valid family members. Approach (Option B, builder-level — the originally-planned direction): bind shrinks the builder's declared param surface (schema.params) and wraps its build closure to re-splice the captured constant at its original positional slot. The construction layer (collect_params / lower_items / param_space / compile_with_params) is byte-unchanged: both dock sites already key off builder.params(), so the shrink propagates for free. Addressing is by param name (the by-name authoring address space, C23/0032); the compilat stays wired by raw index — names never reach it. bind enforces "exactly one open param matches" itself (collect-all-then-reject, panic on zero / on duplicate), mirroring the resolve binder's posture, because a node's own schema.params is not covered by check_param_namespace_injective. Chained binds reconstruct the correct positional vector because each layer computes its slot index relative to the param list it sees (no global position table). Considered and rejected: a per-node builder_const(N) constructor (explodes combinatorially over which subset is constant, not dynamic); a ParamBinding overlay struct on Composite (would need new bookkeeping the builder-level form avoids). Verified: cargo build/clippy/test --workspace all green; new tests cover slot removal, positional reconstruction (chained + partial, via eval), the three panic paths, and the composite param_space projection; blueprint.rs construction layer confirmed byte-unchanged. Deferred (out of scope, follow-up issue): exporting a named frozen strategy as a blueprint-as-values collection — the issue's third fork, needs its own brainstorm. closes #55 |
||
|
|
3413194809 |
plan: 0034 blueprint-constant-bind
Task decomposition for PrimitiveBuilder::bind (spec 0034): T1 the method + aura-core unit tests (Probe node drives positional reconstruction RED→GREEN; three should_panic), T2 node-vehicle coverage (Sma/LinComb via eval in aura-std), T3 Composite param_space coverage + construction-layer zero-change guard + workspace gate. Crate-topology aware: aura-core unit tests use a local Probe, not Sma/LinComb (no upward dep). refs #55 |
||
|
|
9e57caebb8 |
spec: 0034 blueprint-constant-bind (boss-signed)
Add PrimitiveBuilder::bind(slot, value): fix a node param as a structural constant, removed from param_space entirely (not pinned in the injected vector). Closes the gap #55 names — param-bearing nodes (Sma/Exposure/LinComb) had no constant path, only the no-sweep-mode SimBroker precedent. Settled direction (Option B, builder-level): .bind() shrinks the builder's declared param surface and wraps its build closure to re-splice the constant; the construction layer (collect_params/lower_items/param_space) is unchanged because both already key off builder.params(). Addressing is by-name (authoring address space, C23/0032); compilat stays index-wired. Auto-signed under /boss spec_auto_sign: objective gates green, grounding-check PASS, unanimous five-lens spec-skeptic panel SOUND (one editorial repair round: corrected the harness param_space assertion and pinned the exactly-one-match bind contract). refs #55 |
||
|
|
52e0214adb |
audit: cycle 0033 (#54) — refresh stale JSON-writer cross-refs after to_json→serde
Architect drift review over 065cf1d..HEAD. Code + contracts hold; the only drift was documentation cross-references made stale by retiring RunReport's hand-rolled to_json. Contracts (all hold): - C14: to_json still emits canonical, flat, deterministic JSON — now serde-produced. model_to_json (the graph-model JSON) stays hand-rolled and was correctly untouched. - C16: serde_json dev-dep → normal-dep in aura-engine is squarely sanctioned by the amended per-case policy (INDEX.md:576-583) — a vetted standard crate used where it does the job (incl. the frozen bot), removing a hand-rolled writer. No gap. - C1: report types have no HashMap; serde is declaration-order/deterministic; the r1.to_json()==r2.to_json() asserts and the new to_json_equals_serde_disk_shape pin stay green. Regression: none configured (profile regression: []) — no-op; architect is the gate. Resolution — fix (3 prose cross-refs, fixed inline this commit): - graph_model.rs:5 (module doc) — claimed the model JSON is in the "RunReport::to_json house style (no serde)"; to_json is serde now. Reframed: the model JSON is hand-rolled (no serde), and is the engine's last hand-rolled JSON writer since 0033. - graph_model.rs:30 (json_str doc) — cross-referenced RunReport::json_str, deleted this cycle. Reworded to describe the escape set directly (graph_model's own json_str stays). - docs/design/INDEX.md:729 — "golden-tested like RunReport::to_json" framed the hand-rolled model JSON by a now-serde to_json. Reframed to name model_to_json as the surviving hand-rolled writer. Cycle 0033 drift-clean after this tidy. Not a milestone close (the param-sweep milestone fieldtest is a separate deliberate act). Gates: clippy --workspace --all-targets -D warnings clean (doc-only edits, compile-inert). refs #54 |
||
|
|
f1d0bf00ec |
fix(aura-engine): unify RunReport stdout JSON with the on-disk serde shape
RunReport had two divergent JSON encoders: the registry serialized to disk via serde_json (params as an array-of-pairs, floats as 2.0), while stdout rendered through a hand-rolled to_json (params as an object, whole floats as 2). The same record was a structurally different document on disk than on the wire, and no test pinned their relationship — most visibly, `aura runs list` read a record from disk via serde and reprinted it in a different shape. Retire the hand-rolled writer: RunReport::to_json now delegates to serde_json::to_string(self), so stdout is byte-identical to the runs.jsonl line for the same record. The hand-rolled json_str helper is deleted (the graph_model.rs json_str is a separate symbol, untouched — the graph-model JSON is out of scope, #58/#28). A new pin test, to_json_equals_serde_disk_shape, asserts to_json() == serde_json::to_string(&report) so the two encoders can never silently diverge again. Observable change: stdout `params` is now an array-of-pairs and finite f64 fields carry a fractional part (2 -> 2.0); the nested envelope, field order, window [from,to], and integer-valued seed/exposure_sign_flips are unchanged. The on-disk shape does NOT change — only stdout moves to match disk. serde_json is promoted from a dev-dependency to a normal dependency of aura-engine (to_json is non-test code). Under the amended C16 per-case policy this passes: serde_json is a vetted standard crate already in the workspace (registry, ingest), deterministic (C1-safe, already relied on for the disk path), and is exactly "the vetted standard crate doing what the code would otherwise hand-roll" — it removes the last hand-rolled RunReport JSON writer. Goldens flipped to the serde shape: the engine canonical-form golden, the aura-cli sweep golden (cli_run.rs), and the aura-cli odometer-order sweep golden (main.rs) — the last was not in the plan's inventory; the cargo test --workspace gate surfaced it and it took the identical transformation. Structural CLI asserts and the r1.to_json()==r2.to_json() determinism asserts are shape-agnostic and stay green. Spec docs/specs/0033, plan docs/plans/0033, boss-signed (unanimous 5-lens panel). Two stale prose cross-refs (graph_model.rs:30, docs/design/INDEX.md:729) deferred to cycle-close audit. Gates: cargo build/test --workspace green, clippy --all-targets -D warnings clean. closes #54 |
||
|
|
e2056b6436 |
plan: 0033 unify-runreport-json-serde
Two-task RED→GREEN plan for spec 0033. Task 1 (RED, test-only): flip the engine canonical-form golden + the CLI sweep golden to the serde array-of-pairs/.0-float shape, add the to_json_equals_serde_disk_shape pin test — all fail against the current hand-rolled to_json. Task 2 (GREEN, atomic): promote serde_json dev-dep -> normal dep, swap to_json's body to serde_json::to_string, delete the now-unused json_str helper, refresh the method + module-header doc comments — one build+clippy gate flips every Task-1 assertion green. graph_model.rs:30 + INDEX.md:729 stale cross-refs deferred to cycle-close audit. refs #54 |
||
|
|
c8634d7fa9 |
spec: 0033 unify-runreport-json-serde (boss-signed)
Settled-source direct entry from issue #54 (no brainstorm). Retire the hand-rolled RunReport::to_json writer and render stdout through serde, so a record's stdout JSON is byte-identical to its runs.jsonl line, and pin that identity with a test. Removes the last hand-rolled RunReport JSON writer (amended C16). Disk shape unchanged; graph-model JSON (model_to_json) out of scope (#58/#28). Auto-signed under /boss spec_auto_sign: objective gates green (precondition clean, self-review clean, grounding-check PASS) + unanimous 5-lens spec-skeptic panel (criterion/grounding/scope-fork/ambiguity/plan-readiness all SOUND). The grounding juror re-ran serde_json 1.0.150 against the canonical-form fixture and confirmed the spec's "After" bytes. The one C16-scrutinized consequence (serde_json dev-dep -> normal dep in aura-engine) is named and justified in the spec; scope-fork juror confirmed it is settled by amended C16, not a silent pick. refs #54 |
||
|
|
065cf1df1e |
audit: cycle 0032 (#59) — drift-clean; param_space() injectivity is a compile invariant
Architect drift review over 11dfff8..HEAD: clean, no drift, no debt. - C9/C12/C19/C23 ledger prose matches the landed code exactly. The C9 refinement names the retired fan-in machinery (signature_of, leaf_has_param, check_fan_in_distinguishability, check_composite_fan_in) as retired, names the two intentionally-dropped render-identity cases (asymmetric param/paramless, role-vs-leg) as deliberate drops, and points at DuplicateParamPath/.named(). - C23 honoured: the check reads only the param_space() boundary name projection; the compilat stays index-wired, no name became load-bearing. - C1 preserved: param_space() stays infallible; the gate is pure pre-build structural validation; injective blueprints compile bit-identically. - Drift hazards all clear: no live reference to the retired machinery (only frozen historical specs/plans/fieldtests + the cycle's own test-doc comments); the stale "CLI render" signature_of doc line was removed with the function; both resolve/resolve_axes callers check injectivity before resolving so the unreachable! arms are genuinely dead; no live contract references the removed IndistinguishableFanIn / AmbiguousKnob variants; engine core untouched. Regression: none configured (profile regression: []) — architect is the gate. Resolution: carry-on (drift-clean). Gates re-run by the orchestrator: cargo build/test --workspace green (0 failed), clippy --all-targets -D warnings clean. Drift-clean is not a milestone close (the milestone fieldtest gate is separate). |
||
|
|
1b7e4ad169 |
feat(aura-engine): param_space() injectivity as a compile invariant
A non-injective param_space() — two slots under one path-qualified name — is the by-name knob address space (C12/C19) being broken: no binding can select one slot without the other. This cycle makes it a structural compile error. One check, check_param_namespace_injective over the param_space() names, replaces the whole fan-in machinery (signature_of, leaf_has_param, check_fan_in_distinguishability, check_composite_fan_in) and runs from compile_with_params AND from both binders before resolve/resolve_axes — so the canonical by-name author sees the structural cure (CompileError::DuplicateParamPath, carrying the path and pointing at .named(...)) instead of the AmbiguousKnob symptom (#59). With an injective space guaranteed before resolve, no bound name can multi-match, so BindError::AmbiguousKnob is dead and removed (both resolver arms -> unreachable!). param_space() stays infallible; the invariant lives in the compile step (C23). The check is strictly the by-name-addressability property. The old fan-in predicate (equal signature AND >=1 param) also rejected two collisions that are NOT param_space duplicates — an asymmetric param/paramless collision and a role-vs-leg collision — which guarded render identity, dead since the renderer was retired in 0026. Both are intentionally dropped; a future node-/wiring-name check, if wanted, is decoupled from injectivity. The new invariant correctly rejected a pre-existing fixture (multi_output_composite's two unnamed Sma legs); cured in place with the .named(fast/slow) the invariant mandates. A new E2E test drives the cured cross through the by-name binder end-to-end (resolve + bootstrap + run to a populated exposure trace). C9/C12/C19/C23 ledger notes amended. Verified: cargo build/test --workspace green (0 failed), clippy --workspace --all-targets -D warnings clean. closes #59 |
||
|
|
770cf7177d |
plan: 0032 param-namespace-injectivity
Three tasks for the single-iteration cycle: - Task 1 (RED): add CompileError::DuplicateParamPath + the three new tests (non-fan-in duplicate, asymmetric nested collision, by-name #59 regression), verified failing against current code. - Task 2 (GREEN): add check_param_namespace_injective, wire it into compile_with_params + both binders (before resolve), remove the fan-in machinery (signature_of/leaf_has_param/the two check fns) + IndistinguishableFanIn + AmbiguousKnob (both resolver arms -> unreachable!) + the lib.rs re-export, migrate tests 1/2, remove tests 7/8 + macd fixture. One cargo build --workspace gate over the whole enum-coupled change. - Task 3: amend the C9/C12/C19/C23 ledger notes. refs #59 |
||
|
|
7d04cb313a |
spec: 0032 correct test 5 fixture nesting (boss-signed)
Test 5's asymmetric-collision fixture asserted a root-level param_space
["sma.length"] while also being "test 2's fixture with one leg swapped" (nested)
— a contradiction: check_fan_in_distinguishability only descends into composites,
so a root-level collision is not rejected today (not RED-first). Caught at plan
time while writing the verbatim fixture body.
Corrected: test 5 nests the asymmetric collision in an inner composite `asym`
(param-bearing Sma "sma" + paramless Pass1 .named("sma") + Sub, both leaves on
role price fanning into the Sub) under a source-bound root, asserting
param_space() == ["asym.sma.length"] (the single injective entry — the paramless
leg contributes no path) and that compile_with_params(&[I64(2)]) is Ok after.
Re-grounded PASS, re-panel unanimous SOUND. Forward correction on top of
|
||
|
|
7f059d0d94 |
spec: 0032 param-namespace-injectivity (boss-signed)
param_space() injectivity becomes a stated, enforced compile invariant. One structural check (check_param_namespace_injective) replaces the fan-in machinery (check_fan_in_distinguishability, check_composite_fan_in, signature_of, leaf_has_param) and the AmbiguousKnob binder branch. The error is path-carrying (DuplicateParamPath) and the binders run the check before name resolution, so the canonical by-name author sees the structural cure (.named(...)) instead of the AmbiguousKnob symptom. param_space() stays infallible; the invariant lives in the compile step (C23). Two render-identity-only fan-in rejections (asymmetric param/paramless collision, role-vs-leg signature collision) are intentionally dropped — neither is a param_space duplicate (every knob keeps a unique path), and the property they guarded (render distinguishability) has had no live consumer since the renderer was retired in 0026. The larger is pinned by a now-compiles fixture. Boss-signed under spec_auto_sign: objective gates green (precondition clean, self-review clean, grounding-check PASS without override) + a unanimous five-lens spec-skeptic panel. The grounding lens first BLOCKed a false set-equality claim; the user ratified dropping both render-identity cases, the spec was reframed onto the injectivity scope, re-grounded PASS, and the re-panel returned unanimous SOUND. refs #59 |
||
|
|
11dfff860c |
fieldtest: cycle 0031 node-naming — naming cure under-signposted on the by-name flow
Per-cycle fieldtest of node-instance naming (#56), driven as a downstream consumer from the public interface only (ledger + spec + rustdoc + CLI; no crates/*/src read), three fixtures built and run from HEAD. Verdict: the core 0031 promise holds first-try. A consumer authors Sma::builder().named("fast"), inspects param_space() (sma_cross.fast.length), binds by name and runs; the default-name case (sma.length / exposure.scale, verbatim lowercase) and paramless-interchangeable-stays-legal both hold. 0 bugs, 3 working, 2 friction, 1 spec_gap. The one real gap (verified by the orchestrator against the fixture output): the spec's headline forcing function IndistinguishableFanIn does NOT reach an author on the canonical .with(...).bootstrap() flow. An un-named 2-SMA cross emits a literal DUPLICATE knob (sma_cross.sma.length x2); the binder resolves names before the compile fan-in check, so the author hits UnknownKnob / AmbiguousKnob("sma_cross.sma.length") — which point at the knob, not at the cure "name your nodes". IndistinguishableFanIn only surfaces via the positional compile_with_params path. Rejection still happens (no invalid blueprint runs), so it is an ergonomic/signposting gap, not a correctness bug. Routed to the backlog (relates to #58); 0031 stays audit-closed. Minor: FlatGraph/Harness lack Debug, so a bootstrap Result can't be {:?}-printed. |
||
|
|
0e411f1796 |
audit: cycle 0031 (#56) — drift fixed (stale C9 render prose); node-instance naming
Architect drift review of cycle 0031 (node-instance naming; spec |
||
|
|
ffed8cc612 |
feat(aura-core,aura-engine,aura-cli): node-instance naming retires ParamAlias
Every blueprint node now carries a name. A primitive builder gains an optional
instance name (Sma::builder().named("fast")); when omitted it defaults to the
node's type label, ASCII-lowercased verbatim ("SMA"->"sma", "SimBroker"->
"simbroker"). param_space() is now uniformly <node>.<param> at every level
including the root (sma_cross.fast.length, exposure.scale). Fan-in
distinguishability (C9) and signature_of re-key onto the node name: two same-type
siblings both defaulting to "sma" collide and IndistinguishableFanIn fires, so
one act -- naming the legs "fast"/"slow" -- fixes both the naming collision and
the fan-in check. The index-addressed ParamAlias overlay, Composite.params, the
Composite::new 5th argument, aliases_on, and check_alias_indices are removed
(Role.name and OutField.name untouched). Ledger C9 and C23 amended.
This is why the change is correct, not merely convenient: blueprints are
value-empty (C11), so the thing that would otherwise distinguish two SMAs --
their length -- is not present at construction; identity must come from a name,
not a deferred value. Closes the #56 friction surfaced by the param-space &
sweep milestone fieldtest (a freshly-authored 2-SMA cross was unbindable and
would not bootstrap without two hand-counted ParamAlias overlays).
Two plan defects were corrected during implementation and verified:
- the three new fan-in/path tests authored sma_cross at root level, which would
qualify to "fast.length" (root prefix is empty) and is not the nested case the
fan-in check inspects; nesting sma_cross under a root (a shared
sma_cross_under_root helper) restores the asserted "sma_cross.fast.length" and
IndistinguishableFanIn{node:2};
- three cycle-0030 named-binder tests bound the real harness by the old names
(sma_cross.fast / scale) and were migrated to the new path strings, surfaced by
the workspace test gate.
Verified by the orchestrator: cargo build/test --workspace green (198 tests,
0 red), clippy --all-targets -D warnings clean. model_to_json emits the type
label + factory param names (node-name-independent), so sample-model.json and the
graph_model golden are byte-identical (untouched). NodeSchema gained a Default
derive (the builder default-name test constructs an empty schema). fieldtests/
are frozen non-workspace records, not migrated.
closes #56
|
||
|
|
d8900900b5 |
plan: 0031 node-instance naming
Implementation plan for spec 0031 (#56). Three tasks: 1. aura-core PrimitiveBuilder instance-name mechanism (.named()/node_name(), default = lowercased type label) — additive. 2. The engine mechanism + the full compile-coupled migration, as one task by necessity: collect_params node segment, signature_of + fan-in re-keyed onto the node name, ParamAlias / Composite.params / the Composite::new 5th arg / aliases_on / check_alias_indices removed, every in-workspace Composite::new caller and ParamAlias reference migrated, the mirror / top_level (inverted) / signature / CLI-golden assertions updated — all under one cargo build/test --workspace gate that enumerates migration completeness. 3. Ledger C9 + C23 amendments. plan-recon (DONE_WITH_CONCERNS) surfaced two sites the brief missed, both now in the plan: the sweep-golden twin crates/aura-cli/tests/cli_run.rs:214 (lockstep with main.rs), and the model_to_json goldens — resolved as OUT of scope (the JSON model emits the type label + factory param names, both node-name-independent, so sample-model.json and the graph_model.rs golden stay byte-identical; the fixtures migrate structurally only). signature_of stays -> String, re-keyed onto the node name. fieldtests/ are frozen non-workspace records, out of scope. refs #56 |
||
|
|
581849766f |
spec: 0031 node-instance naming (boss-signed)
Design spec for #56 — give every blueprint node a name so param-space path-qualification and fan-in distinguishability both flow from one author-controlled identity, and retire the index-addressed ParamAlias overlay. A primitive builder gains an optional name (default = the node's type-label string, ASCII-lowercased verbatim: "SMA"->"sma", "SimBroker"->"simbroker"; explicit names must be non-empty). param_space() becomes uniformly <node>.<param> at every level including the root. signature_of and the C9 fan-in rule re-key onto the node name: two same-type siblings both defaulting to "sma" collide and IndistinguishableFanIn fires correctly, so one act — naming the legs "fast"/"slow" — fixes both the naming collision and the fan-in check. ParamAlias / Composite.params / the alias index-check are removed (Role.name and OutField.name untouched). Ledger C9 and C23 amended. One cohesive iteration: the engine mechanism + in-workspace migration (CLI sample, engine tests, sweep goldens, graph_model.rs, sweep.rs, lib.rs re-export) land together since removing the struct is compile-coupled; fieldtests/ are frozen non-workspace records, out of scope. Boss-signed under spec_auto_sign: all objective gates green (precondition, self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel (criterion / grounding / scope-fork / ambiguity / plan-readiness) returned SOUND after one fix round. The panel's first round caught real defects — a worked example written against a non-existent fluent API, a false [param:] render-marker claim (the marker was removed in cycle 0020), and an under-specified compound default name — all corrected and re-grounded before the unanimous pass. refs #56 |
||
|
|
03285d1b47 |
fieldtest: milestone the-world param-space & sweep — green, delivers promise
Milestone-scope fieldtest (closing functional gate) for "The World —
parameter-space & sweep" (issues #30-#36). Three curated end-to-end scenarios
derived top-down from the milestone promise, driven from the public interface
only (ledger + specs + rustdoc; no crates/*/src read), built and run from HEAD
via a downstream consumer crate.
Delivery verdict: GREEN. The milestone delivers its promise.
- mw_1 single run bound by name: .with("sma_cross.fast", 2).bootstrap() binds
and runs (12 equity rows); diagnostics precise and knob-named (UnknownKnob /
AmbiguousKnob / KindMismatch).
- mw_2 named-axis sweep + compare: .axis(..).sweep(run) yields a 4-member,
ordered (odometer), disjoint, non-constant SweepFamily; ranking from public
fields. CLI (aura sweep / aura runs rank) delivers the same with no Rust.
- mw_3 structural-constant negative space (deliberate): SMA2-entry's
definitional `2` has no honest expression today -> grounds #55. Out of
milestone scope; not a blocker.
Findings: 0 bugs, 2 working, 3 friction, 1 spec_gap. Verified independently
(re-ran all three from HEAD; confirmed a plain SMA-cross emits sma_cross.length
twice and the shipped CLI sample carries the fast/slow ParamAlias overlays).
Friction (routed to the forward queue, none blocks the gate):
- the promised sma_cross.fast/.slow names are not free — they require
author-added ParamAlias overlays, doubly mandatory because the canonical
2-SMA cross also will not bootstrap without them (IndistinguishableFanIn);
- the sweep closure drops back to positional bind + manual name re-zip.
spec_gap -> #55: the wished-for structural-constant consumer code is recorded
in mw_3 as #55's acceptance evidence.
This commit is the milestone fieldtest itself; it closes no issue. Closing the
tracker milestone stays a deliberate manual act on this green gate.
|
||
|
|
acaa6b0df8 |
audit: cycle 0030 (#35) — drift-clean; named param binding
cycle 0030 tidy (clean). The architect drift review against docs/design/INDEX.md
and CLAUDE.md found the cycle drift-clean: every load-bearing contract the named
binding layer touches holds.
- C1 / C12 / C19 / C23: a pure authoring layer — sweep.rs, the lowering path,
collect_params, and param_space() are unchanged; with/axis/Binder/SweepBinder/
resolve/resolve_axes all terminate into the existing bootstrap_with_params /
GridSpace::new / sweep. No new state, no concurrency, no path into the run loop.
Equivalence pinned by named_binder_runs_bit_identical_to_positional and
named_axes_grid_parity_with_positional. The match key is the exact param_space()
name (p.name == name), read-only.
- C7: pure-equality kind check (v.kind() != p.kind), no coercion.
- C16 / C10: std-only (the
|
||
|
|
937257e368 |
feat(aura-engine,aura-cli): named param binding — sweep axes .axis()/.sweep() (0030 iter 2)
Bind a sweep's axes by name instead of by a positional GridSpace:
bp.axis("sma_cross.fast", [2, 3]).axis("sma_cross.slow", [4, 5]).axis("scale", [0.5]).sweep(run)
The sweep half of spec 0030, completing the named-binding feature (#35). A pure
authoring layer over the existing GridSpace::new / sweep primitives; engine core
untouched (C1/C12/C19/C23).
This iteration:
- resolve_axes(): shares resolve's name->slot mapping (iter 1), adds the EmptyAxis
check (the variant defined in iter 1 is now first constructed here) and a
per-element axis kind-check (every element of each axis, first offending element
in axis order). Its validation is a strict superset of GridSpace::new's
(arity / non-empty / per-element kind), so SweepBinder::sweep's downstream
GridSpace::new(...).expect(...) is infallible by construction — it can never
panic on author input.
- Composite::axis -> SweepBinder -> SweepBinder::sweep (Result<SweepFamily, BindError>).
- The CLI sample sweep converted to the named form (behaviour-preserving).
Plan correction (verified by the orchestrator): the iteration plan said only the
GridSpace import was orphaned by the CLI conversion, but replacing the free
sweep(&grid, ..) call with the .sweep(..) method also orphans the free `sweep`
import — both were dropped from aura-cli to satisfy clippy -D warnings. The Scalar
import (from aura_core) stays used and was untouched.
Verified (run by the orchestrator): 6 new aura-engine tests green —
resolve_axes round-trip, EmptyAxis, MissingKnob, the per-element KindMismatch on a
mixed axis (the superset/no-panic guarantee), a builder no-panic wrong-kind test,
and a GridSpace .points() parity test; the two sweep JSON goldens
(sweep_report_renders_four_points_in_odometer_order + the cli_run integration
golden) green unchanged (name/value/order-preserving conversion); full
`cargo test --workspace` green and `cargo clippy --workspace --all-targets
-- -D warnings` clean.
refs #35
|