Commit Graph

115 Commits

Author SHA1 Message Date
Brummel 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
2026-06-11 11:51:30 +02:00
Brummel 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
2026-06-11 11:29:57 +02:00
Brummel 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
2026-06-11 11:20:22 +02:00
Brummel 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.
2026-06-11 09:11:37 +02:00
Brummel abd76c9756 plan: 0030 named param binding — sweep axes (iteration 2)
Iteration-2 plan for spec 0030 (the sweep side): two tasks.
- Task 1 (aura-engine): resolve_axes (shares resolve's name->slot mapping; adds the
  EmptyAxis check and a per-element axis kind-check, a superset of GridSpace::new so
  the downstream .expect() is infallible), Composite::axis / SweepBinder /
  SweepBinder::sweep, the SweepBinder re-export, and the engine tests (round-trip,
  EmptyAxis, MissingKnob, per-element KindMismatch on a mixed axis, a builder
  no-panic wrong-kind test, and a GridSpace .points() parity test). RED-first via a
  todo!() resolve_axes stub. EmptyAxis is now constructed (was defined in iter 1).
- Task 2 (aura-cli): convert sweep_family() grid construction to .axis(...).sweep(...)
  (keeping the per-point closure and the local `space` it reads for manifest params);
  drop the now-orphaned GridSpace import (the clippy -D warnings trap this iteration).

The sweep JSON param goldens (main.rs + tests/cli_run.rs) are name/value/order-
preserving and stay green unchanged. Engine core untouched (C1/C12/C19/C23).

refs #35
2026-06-11 00:24:12 +02:00
Brummel 7cd990f789 plan: 0030 named param binding — single-run (iteration 1)
Iteration-1 plan for spec 0030 (the single-run side): two tasks.
- Task 1 (aura-engine): BindError vocabulary, the shared `resolve` core
  (two-phase total error order), Composite::with / Binder / Binder::bootstrap,
  the lib.rs re-export, and the engine tests (resolve round-trip, one per
  single-run BindError variant, two precedence tests, the C1 named≡positional
  equivalence test). RED-first via a todo!() resolve stub, GREEN on impl.
- Task 2 (aura-cli): convert the sample single-run test to .with(...).bootstrap()
  using the exact param_space() names (sma_cross.fast/sma_cross.slow/scale).

The full BindError enum (incl. EmptyAxis) is defined now and re-exported, so the
unconstructed sweep-only variant is reachable API (not dead_code) — EmptyAxis is
constructed in iteration 2. Engine core untouched (C1/C12/C19/C23).

refs #35
2026-06-11 00:09:29 +02:00
Brummel 8fae58fdc4 spec: 0030 named param binding (boss-signed)
Bind a blueprint's open knobs by name via a fluent builder instead of a
positional, Scalar-wrapped Vec in param_space() order: a single run as
bp.with("sma_cross.fast", 2).with("scale", 0.5).bootstrap(), a sweep as
bp.axis("sma_cross.fast", [2,3]).axis("scale", [0.5]).sweep(run). A pure
authoring layer over the existing bootstrap_with_params / GridSpace / sweep
primitives; the engine core is untouched (C1/C7/C12/C19/C23 preserved).

Ratified design (brainstorm -> specify):
- Fluent builder (.with()/.axis()), not a macro — C10 builder-API idiom.
- Raw literals via Into<Scalar>; the literal fixes the variant (2->I64,
  0.5->F64); kind-check is pure equality, no coercion.
- Match key = the EXACT param_space() name (user's Option 1): path-qualified
  for composite-interior knobs (sma_cross.fast), bare for root-level knobs
  (scale). Short bare names are an authoring choice (promote to a root leaf),
  not a job of this layer; no engine change, no unqualified matching.
- .with() not .bind() — bind is reserved for #55's structural-constant overlay.
- Total error order (a-f) over a single BindError vocabulary: Phase 1 validates
  bindings (UnknownKnob/AmbiguousKnob/EmptyAxis/DuplicateBinding), Phase 2 walks
  slots (MissingKnob/KindMismatch); first failing check wins; sweep kind-check is
  per-element, making resolve_axes a superset of GridSpace::new so the downstream
  .expect() is infallible.
- Two iterations: single-run side, then sweep-axis side (shared name-resolution
  core). The CLI sample stays nested (user's call); the worked example shows both
  qualified and bare names honestly, no fixture change.

Auto-signed under /boss spec_auto_sign after the objective gates (precondition,
parse no-op, grounding-check PASS) and a unanimous five-lens spec-skeptic panel.
The panel hardened the spec across five rounds: it corrected the worked-example
names to the real param_space() output, pinned the exact-name match key, and made
the error precedence a total order closing a sweep per-element panic path. The
literal-inference grounding gap was closed separately by e97906a. Two design
points were taken by the user directly (Option 1; keep the sample nested).

refs #35
2026-06-11 00:00:55 +02:00
Brummel 654b23ad1f audit: cycle 0029 (#33) — run registry; one ledger drift fixed
Cycle-close architect review of 16e31fe..HEAD against docs/design/INDEX.md +
CLAUDE.md. Regression gate: none configured (commands.regression: []), so the
architect is the sole gate.

What holds:
- C18 honoured — aura-registry stores (manifest, metrics) RunReports append-only
  to runs/runs.jsonl, reproducible-from-manifest; no git/Gitea duplication, no
  multi-project manager. The depth deferrals (lineage, re-derive, run-diff,
  run_id, Aura.toml runs-dir) are named in the spec, not silently dropped.
- C1 preserved — the sweep stays disjoint/lock-free; "sweep == N independent
  runs" now compares full RunReports; serde_json output is deterministic.
- C12/C19/C20 fit — SweepPoint.report closes cycle C's deferred manifest-per-
  point gap; the manifest stays World-supplied (engine `sweep` is `Fn -> RunReport`,
  domain-free).
- Workspace green (cli_run 11, registry 5, engine 93, …); clippy clean.

Resolution:
- FIX (this commit): docs/design/INDEX.md "External components" (the data-server
  row) still asserted the struck "external-dependency firewall / engine crates
  stay external-dependency-free" framing — stale after the C16 amendment (aura-
  core/aura-engine now link serde). Rewritten to the amended per-case policy.
  The iter-1 commit's "no ledger text still asserts it" missed this mirror; it is
  now true (re-grep clean across the ledger and live source).
- FOLLOW-UP (#54, idea): RunReport has two divergent JSON encoders — serde for
  the on-disk store, hand-rolled to_json for stdout. This was a deliberate,
  spec-named deferral (keep stdout goldens stable this cycle); filed for the
  stdout→serde unification that retires the last hand-rolled writer.

Ratify: the C16 amendment (blanket zero-dependency -> considered per-case policy)
is the intentional contract change of this cycle, justified in the 0029 spec and
the iter-1 commit; this audit ratifies it as drift-clean (the codebase no longer
contradicts it anywhere).

closes #33
2026-06-10 20:41:44 +02:00
Brummel 2811c060dc plan: 0029 run-registry CLI persistence + read surface (iteration 3)
Final iteration of cycle D: aura sweep persists each point's RunReport to
runs/runs.jsonl; aura runs list prints every stored record; aura runs rank
<metric> prints them best-first. sweep_report splits into a production
sweep_family() + a #[cfg(test)] renderer; process goldens run in a temp cwd so
persistence never dirties the repo; /runs/ is git-ignored.

refs #33
2026-06-10 20:33:57 +02:00
Brummel 5fa003da64 plan: 0029 run-registry engine + registry (iteration 2)
Iteration 2 of cycle D: SweepPoint carries a full RunReport (closure bound ->
RunReport), all engine + CLI callers rethreaded in lockstep so the workspace
returns green; the hand-rolled sweep_point_to_json retired (each point prints
via RunReport::to_json); new aura-registry crate (open/append/load/rank_by +
RegistryError), built and unit-tested but not yet CLI-wired. The aura sweep
goldens pin the per-point manifest params, not the volatile git-HEAD commit.

refs #33
2026-06-10 20:22:18 +02:00
Brummel eec876975c feat(engine): serde foundation + amend C16 dependency policy (cycle D / iter 1)
Iteration 1 of the run-registry cycle (#33): lay the dependency + serde
foundation the registry's typed read-path needs, and amend the contract that
forbade it.

Contract change (load-bearing — C18/C16). C16's blanket "zero-external-
dependency by commitment" + "aura-ingest is the sole external-dependency
firewall" framing is struck and replaced by a considered, per-case dependency
policy: dependencies are admitted by deliberate review (what a crate pulls in
vs. what it buys), with particular scrutiny for anything entering the frozen
deploy artifact (C13); standard vetted crates (serde, rayon, ...) pass that
review and are used wherever they do the job, including in the bot; hand-rolling
what a vetted crate does is the anti-pattern. C16's engine/project split +
three-tier node reuse are unchanged. The five source comments that asserted the
struck framing (aura-engine/aura-ingest Cargo.toml + aura-ingest/report.rs docs)
are rewritten to match; no live source or ledger text still asserts it.

Per-case justification for serde (the policy now requires one): it gives the
run-report types a typed (de)serialization path — exactly what ranking/compare
over stored runs needs, and what the writer-only hand-rolled JSON (C14) could
never provide; closes the typed-read-path gap (#17). Its closure is tiny,
ubiquitous, heavily audited, and its output deterministic (C1-safe). serde_json
is test-only this iteration (dev-dependency); no production serde_json yet.

Changes: [workspace.dependencies] serde (derive) + serde_json; serde derives on
Timestamp (aura-core) and RunMetrics/RunManifest/RunReport (aura-engine), with
serde_json round-trip tests (window renders as a [from,to] integer array via the
transparent Timestamp newtype). The hand-rolled to_json writers are untouched
(still drive stdout). No aura-registry crate, no SweepPoint change — those are
iterations 2 and 3.

Verified: cargo test --workspace green (incl. the two new round-trip tests);
clippy --workspace --all-targets -D warnings clean; no surviving assertion of
the struck zero-dep framing in live source or the ledger.

refs #33
2026-06-10 20:11:39 +02:00
Brummel 8159b785a8 plan: 0029 run-registry foundation (iteration 1)
Iteration 1 of cycle D: amend C16 (blanket zero-dep -> per-case policy) in the
ledger + the five source comments asserting the struck framing; add serde/
serde_json workspace deps; derive Serialize/Deserialize on Timestamp and the
RunMetrics/RunManifest/RunReport report types, with serde_json round-trip tests.
No CLI change, no aura-registry crate, no SweepPoint change (those are iter 2/3).

refs #33
2026-06-10 20:05:20 +02:00
Brummel d309336a72 spec: 0029 run-registry — index & compare a sweep family
Cycle D of the milestone "The World — parameter-space & sweep" (#33). A
persistent, append-only run registry: each run's (manifest, metrics) is a
record in runs/runs.jsonl; a read surface lists all records and ranks them
best-first by a named metric — C18's "compare experiments over time", which
has no home in git or Gitea. First cut = persist + list + rank; promotion/
status, lineage, run-diff, on-demand re-derivation, run_id, and Aura.toml
runs-dir wiring are deferred.

Contract-level change: C16's blanket "zero-external-dependency by commitment"
is amended to a considered, per-case dependency policy (scrutinize what enters
the frozen artifact; standard vetted crates like serde/rayon pass that review;
never hand-roll what they do). The engine/project split + three-tier node
reuse of C16 are unchanged. This admits serde/serde_json — the typed read-path
ranking needs, and which closes #17 (typed RunReport handle).

Shape: serde derives on RunManifest/RunMetrics/RunReport + Timestamp;
SweepPoint carries a full RunReport (closing the manifest-per-point gap cycle C
deferred to #33); new crate aura-registry (open/append/load/rank_by); CLI gains
aura runs list / aura runs rank <metric> and aura sweep persists each point.
Three iterations, first planner handoff = iteration 1 (foundation).

Sign-off: spec_auto_sign was enabled for this cycle; the five-lens spec-skeptic
panel returned 3 SOUND + 2 BLOCK (rank-ordering semantics; iteration-cut
compile-ordering + Scalar->f64 coercion). Both BLOCKs were genuine defects,
fixed before sign-off (rank is now best-first per-metric; the closure-type
change lands with its callers in one green iteration; the coercion is shown).
Non-unanimous panel routed the spec to human sign-off, granted by the user.

refs #33
2026-06-10 19:59:27 +02:00
Brummel 7bfcc3b86e plan: 0028 sweep CLI demonstrator (iteration 2)
refs #32
2026-06-10 18:46:13 +02:00
Brummel e2b6ab8e1b plan: 0028 param-sweep grid (iteration 1 — engine primitive)
refs #32
2026-06-10 18:30:47 +02:00
Brummel 0692637b60 spec: 0028 param-sweep grid
A grid-enumerated family of disjoint instances from one value-empty
blueprint (milestone The World, cycle C). Engine-side GridSpace +
sweep() (std::thread::scope, lock-free disjoint per C1) + SweepFamily;
closure-driven so harness-specific metrics glue stays author-side.

refs #32
2026-06-10 18:21:23 +02:00
Brummel 66dff88528 feat(aura-cli): render the graph as a self-contained WASM-Graphviz viewer; retire ascii-dag
Iteration 2 of cycle 0026 (graph render redesign). `aura graph` no longer emits
ASCII: it now prints one self-contained `.html` to stdout — the ported prototype
pin-graph viewer (drill / inline-expand / styled tooltips / breadcrumb) driven by
the deterministic model serializer that shipped in iteration 1, with layout and
SVG done in-browser by Graphviz compiled to WebAssembly. Closes the redesign
opened by spec 0026; unblocked by cycle 0027 (input ports are now named, so the
viewer labels every input pin from the model's real names instead of inventing
"a"/"b").

What ships:
- crates/aura-cli/assets/: the ported graph-viewer.js (prototype genDot/chrome
  verbatim + a normalizeModel adapter mapping aura's real model tuple shape —
  ins = [kind, firing, name], index node keys, `comp` refs, `src_<role>` — into
  the viewer's native shape), plus the vendored Graphviz-WASM (@viz-js/viz@3.7.0)
  and svg-pan-zoom@3.6.1 blobs and a refresh-assets.sh provenance script.
- crates/aura-cli/src/render.rs: render_html(&Composite) -> String, a read-only
  (C9) assembly — model_to_json + include_str! of the inlined assets, no eval,
  no build, no serde (C14). The `["graph"]` arm calls it.
- Retired: the ascii-dag adapter (graph.rs), the `ascii-dag` dependency, the
  `--compiled`/`--macd` graph flag plumbing, render_compiled, the color/terminal
  plumbing, and the 12 ascii-dag-asserting tests + the now-orphaned helpers.
  The cli_run integration test now pins the HTML page envelope.
- crates/aura-core/src/node.rs: the two last ascii-dag prose justifications
  re-grounded (single-line label rule kept, re-justified generically; the
  param-generic rule re-justified on C23, not on a renderer limitation).
- docs/design/INDEX.md: the cycle-0026 C9 realization note (both iterations).

Vendoring decision (spec deferred it to the plan): the WASM/JS blobs are checked
in, not fetched at build time. include_str! needs them at compile time, and a
build-time unpkg fetch would make the build non-hermetic/non-offline and let the
shipped artifact drift with the registry — against C1 (determinism) and C8
(frozen artifacts). ~1.4 MB is the price of a hermetic, frozen render asset.

Three adaptations beyond the plan, each verified: (1) render.rs imports
`aura_engine::Composite` (the path model_to_json takes), not aura_core; (2)
macd_blueprint is now test-only — removing the `--macd` arm left it used solely
by the param-space test, so it took `#[cfg(test)]` rather than removal or an
`#[allow]` suppression (the production `aura run --macd` path is intact, verified
emitting JSON); (3) the `grep ascii-dag` over crates/ matches only the new
contract test, which names the term deliberately to document the retirement — no
stale justification prose remains.

Invariants held (verified independently, not on agent report): C9 (render path
read-only), C10 (viewer is a render asset, no logic/DSL), C4 (four-colour palette
= the four scalar base types), C14 (no serde). The iteration-1 model byte golden
is unchanged and green. cargo build --workspace: 0 errors. cargo test --workspace:
157 passed, 0 failed (incl. the two new HTML tests + the unchanged model_golden).
cargo clippy --workspace --all-targets -- -D warnings: clean. ascii-dag absent
from `cargo tree -p aura-cli`.

closes #51
2026-06-10 17:09:12 +02:00
Brummel c3109bdfdd plan: 0026 graph render viewer (iteration 2)
Iteration-2 plan for cycle 0026 (graph render redesign): port the prototype
viewer, vendor Graphviz-WASM (checked in, for hermetic offline builds), add a
read-only render_html, wire `aura graph` to emit the self-contained HTML, and
retire ascii-dag (adapter, dep, --compiled/--macd flags, 12 ascii-dag tests).
Plus a stale-prose tidy in aura-core and the cycle-0026 ledger note.

refs #51
2026-06-10 16:49:19 +02:00
Brummel e304dbaae1 feat(aura-core): name input ports
PortSpec gains a non-load-bearing `name: String`, so an input port is named just
as FieldSpec.name (output) and ParamSpec.name (param) already are — input ports
were the lone unnamed member of the node signature. Identity stays positional by
slot (C23); the name is render/debug only, never read by bootstrap or the run
loop. PortSpec drops Copy (String is not Copy), exactly as ParamSpec already does.

- Every aura-std node names its input slots: SMA/EMA "series", Sub/Add "lhs"/"rhs",
  Exposure "signal", SimBroker "exposure"/"price" (the slots become
  self-documenting), LinComb "term[i]" and Recorder "col[i]" generated in their
  build loops (mirroring LinComb's existing weights[i] param loop).
- derive_signature carries a composite's Role.name into the derived input port
  (it was dropped before — the output side already carried FieldSpec.name), so the
  graph model is homogeneously named at both levels.
- model_to_json (port_json + the composite-header inputs in scope_json) emits the
  name as a third tuple element: ["f64","any","exposure"]. The byte golden was
  re-captured (machine bytes) and its substring twins updated; the model is now
  fully named across inputs/outputs/params.
- All 16 PortSpec construction sites threaded in one compile-gate change; test
  fixtures carry fixture names. C8 realization note added to the design ledger.

Why name-only, no validation: the name is a pure debug symbol. Wire-by-name was
rejected (it would be a C23 contract change). Bootstrap slot-wiring validation
(which would close #21's same-kind swap footgun) is deferred to its own cycle —
a name alone does not catch the swap; it makes the slots self-documenting and
gives a future validation something to check against.

Verified: cargo test --workspace 168 green; clippy --all-targets -D warnings
clean; cargo build --workspace clean. Read-only render path (C9), no serde (C14),
scalar kinds unchanged (C4).

closes #50
refs #21
refs #51
2026-06-10 16:19:08 +02:00
Brummel 5288249b44 plan: 0027 name input ports
Four tasks for cycle 0027 (spec docs/specs/0027-name-input-ports.md):
1. Add name: String to PortSpec (drop Copy) and thread all 16 construction
   sites in one compile-gate task — aura-std nodes get real slot names, fixtures
   get fixture names, derive_signature carries Role.name; port_json untouched so
   the byte golden stays green and the full suite passes.
2. port_json appends the name; re-capture the model byte golden and update its
   two substring twins (sink_has_empty_outs witnesses a leaf PortSpec.name in the
   model, multi_input_composite witnesses a composite Role.name carried through).
3. Lock the per-node input-slot names (8 aura-std pins).
4. C8 realization note in the design ledger (cross-links C23).

refs #50
2026-06-10 16:04:10 +02:00
Brummel 55cfb29c74 spec: 0027 name input ports
Give a node's input ports a name. PortSpec { kind, firing } is the lone unnamed
member of the node signature today (FieldSpec.name / ParamSpec.name already
exist); a leaf node's input slots have no declared semantics. Add a
non-load-bearing name: String to PortSpec (drops Copy, mirrors ParamSpec), name
every input slot across aura-std nodes + fixtures, carry Role.name into
derive_signature's derived composite port (it is dropped today), and make
model_to_json emit the name in each port tuple. Identity stays positional by
slot (C23); the name is render/debug only, never read by bootstrap or the run
loop.

Surfaced by the 0026 graph-render redesign: a homogeneous pin-graph needs the
name to label input pins instead of inventing "a"/"b". Single iteration.

Design ratified interactively (brainstorm); grounding-check PASS (every
load-bearing current-state assumption ratified by a named green test); parse
gate a no-op (no spec_validation parser configured). Forks resolved: name is a
pure debug symbol, not wire-by-name (C23); String not &'static str (variadic
nodes generate term[i]/col[i]); no bootstrap validation this cycle.

refs #21
refs #50
2026-06-10 15:53:46 +02:00
Brummel bbe2266b9e plan: 0026 graph model serializer
Iteration 1 of the graph render redesign: a read-only model_to_json(&Composite)
in a new crates/aura-engine/src/graph_model.rs, hand-rolled deterministic JSON
(RunReport::to_json house style, no serde). Six tasks: helpers, primitive record,
scope (index keys + synthetic source nodes from bound roles), composite defs
(@role/#N endpoints, distinct-once walk), top-level + byte golden + determinism,
structural assertions + structural-miswire-differs + read-only. Decisions: home
in aura-engine; node keys are indices (C23); input ports carry kind+firing, no
name (per acceptance criterion 3); bound root source-roles minted as synthetic
source nodes.
2026-06-10 11:10:23 +02:00
Brummel cdb6313ee8 spec: 0026 graph render redesign
Replace the ascii-dag aura graph view with a homogeneous pin-graph: aura emits a
deterministic JSON model (hand-rolled, RunReport::to_json house style, no serde);
a vendored viewer JS (genDot, ported from the prototype) turns it into DOT;
Graphviz-WASM lays out + draws it in the browser. aura stays a read-only
serializer (C9), ships no layout engine. The golden-tested contract is the JSON
model. Iteration 1: the model serializer + golden/determinism/read-only tests.
Iteration 2: viewer + WASM vendoring + self-contained HTML, retire ascii-dag.

Grounding-check PASS; design ratified interactively against the 0026 prototype.
2026-06-10 10:58:47 +02:00
Brummel 7f485bbe72 docs(design): graph-render redesign prototype
Interactive, throwaway prototype of the aura graph render redesign — the
visual language ratified for cycle 0026, replacing the ascii-dag text view.
Homogeneous pin-nodes (body + n input pins + m output pins) rendered via
Graphviz-WASM in the browser; pin-to-pin edges, type-as-colour, drill-down +
inline-expand of composites, per-element styled tooltips, source/sink colouring,
top-rank input ordering. index.html is the artifact; the ~1.4 MB WASM dep is
git-ignored and restored by fetch-deps.sh. Visual companion to docs/specs/0026.
2026-06-10 10:58:47 +02:00
Brummel cd569aa6d0 plan: 0025 render root like any composite
Two-task plan for spec 0025 (#49). Task 1 is one compile unit in
crates/aura-cli/src/graph.rs: thread the root composite as stub_ctx (drop the
Option, update both render_graph callers) and name root entries by role.name
instead of source:{kind}; gate is `cargo build -p aura-cli` (goldens stay red
until Task 2 — the golden-recapture exception). Task 2 re-captures
blueprint_view_golden from the live `aura graph` output, updates the SimBroker
needle, adds a root-SimBroker-stub assertion to the macd test and a [src]
role-name-passthrough assertion to reused_composite_defined_once; gate is
`cargo test --workspace` + clippy. compiled_view_golden is the byte-unchanged
negative control (render_compilat path, C23). plan-recon mapped exact line
numbers and flagged the source:{kind} golden twin (blueprint changes /
compiled must not) and the stale, out-of-scope render_clustered.txt fixture.
2026-06-09 17:56:51 +02:00
Brummel ed1d22bbfa spec: 0025 render root like any composite (slot stubs + role-name entries)
Settled source: issue #49, enabled by cycle 0024 (1b39093, root is now a
Composite with input_roles). The cycle removes the last two render special-cases
the root still carries, both pre-0024 vestiges:

(A) render_blueprint threads stub_ctx: None, so a top-level multi-input leaf
    (SimBroker) renders bare [SimBroker] instead of the #… slot stubs an interior
    leaf already gets. Fix: pass the root composite as stub_ctx (it IS a
    &Composite since 0024); stub_ctx drops its Option (both render_graph callers
    pass &Composite). The existing slot_source/fan_in_identifiers/signature_of
    serve a top-level leaf verbatim — no second stub path (#49 acceptance #3).
    -> [SimBroker(#E,#price)] (#E = exposure producer's signature prefix,
    #price = the source role name).

(B) render_blueprint names root entries source:{kind} while render_definition
    names interior entries by role.name — the marker/stub asymmetry. Fix: build
    root entries from role.name (bound-only filter retained), identical to the
    interior. -> [price] instead of [source:F64], byte-symmetric with how the
    same role renders in a where: definition.

render_compilat is left as [source:F64]: post-inline the role name has dissolved
(C23), only the kind survives — [price] pre-inline / [source:F64] post-inline is
C23 made visible, and compiled_view_golden is the negative control.

Scope: crates/aura-cli/src/graph.rs only. Read-only render (C9),
behaviour-preserving for the run path (C1); no engine change. Grounding-check
PASS (10 assumptions ratified against green tests). Render goldens are the
accepted value-asserted regression; closes #49 on land.
2026-06-09 17:47:18 +02:00
Brummel d6f59d7a52 audit: cycle 0024 (#43, #36) — drift-clean; ledger reconciled to the pre-build signature
Architect drift review (862882b..1b39093): NO code drift, no contract weakened.
C1 (behaviour-preserving: 150 tests green, render goldens byte-identical, no
behavioural assertion altered), C8 (sink output: vec![] preserved), C9/C23
(read-only render touches no eval/build; derive_signature's Box::leak is
cold-path-only, leaked names non-load-bearing) all hold. Regression scripts:
none configured (no-op) — the architect review is the gate.

The only drift was design-ledger prose, anticipated and deferred to this audit
by the plan. Resolved here (fix path, orchestrator-applied — docwriter is barred
from the design ledger):

- docs/design/INDEX.md C8 Guarantee: rewritten — a node's signature (NodeSchema)
  is declared pre-build on the value-empty recipe; the built node implements
  lookbacks() (the one param-dependent sizing quantity) + eval(); schema() is gone.
- INDEX.md C8 cycle-0015 note: Blueprint::param_space -> Composite::param_space;
  the #36 lockstep-debt clause marked closed by 0024.
- INDEX.md: added a C8 cycle-0024 realization (signature lives once on the recipe,
  the signature/sizing split, #43/#36 closed) and a C19 cycle-0024 realization
  (struct Blueprint collapsed into the root Composite; Role.source/runnable-iff-
  bound/UnboundRootRole; FlatGraph as the named compilat). Stale symbol names in
  the dated 0016/0017 notes annotated in place with their 0024 renames.
- aura-std/src/lincomb.rs module doc: Blueprint::param_space -> Composite::param_space.

Ledger-gap (four new load-bearing invariants previously unrecorded) closed by the
two new realization notes. Green baseline re-confirmed after the edits: cargo test
--workspace 150 passed / 0 failed; cargo clippy --all-targets -D warnings clean.

refs #43 #36
2026-06-09 13:02:42 +02:00
Brummel c7fc2f6a37 plan: 0024 node signature in blueprint
Five-task, compile-gate-sequenced plan for the behaviour-preserving refactor:
aura-core types (PortSpec/PrimitiveBuilder/NodeSchema/lookbacks) -> aura-std
8 nodes -> aura-engine lib (Composite absorbs Blueprint, signature/derive_
signature, FlatGraph, pre-build validate_wiring, bootstrap reshape) -> engine
tests -> aura-cli/ingest + compile-only render. Per-crate partial build gates;
workspace gate only in the final task. One working-tree unit (committed once
after Task 5 green).

refs #43 #36
2026-06-09 11:30:38 +02:00
Brummel 78d68fa8ad spec: 0024 node signature in blueprint
Consolidate the node data structure so every node's signature (NodeSchema:
inputs/output/params) exists in the blueprint pre-build, declared once.

- Signature vs sizing split: NodeSchema becomes the static signature
  (InputSpec -> PortSpec, lookback removed); the one param-dependent quantity
  (input buffer lookback) moves to a build-time Node::lookbacks() query.
  Node::schema() is removed.
- PrimitiveBuilder (ex-LeafFactory) carries the schema; the built node no
  longer re-declares it -> closes the params-declared-twice drift.
- Blueprint collapses into Composite: Role gains source: Option<ScalarKind>;
  the root is the fully-bound composite (no "main graph"); new error
  UnboundRootRole.
- compile -> FlatGraph -> bootstrap: FlatGraph carries node + signature in
  parallel; bootstrap sizes from lookbacks(), kind-checks from the signatures.
- Renames: BlueprintNode::Leaf -> Primitive, LeafFactory -> PrimitiveBuilder.

Behaviour-preserving (C1). Render is out of scope beyond compiling (next cycle).

refs #43 #36
2026-06-09 11:09:06 +02:00
Brummel 862882bc04 audit: cycle 0023 (#48) — drift reconciled; render paths unified
Architect drift review (bb90c42..481172a): no contract drift. C9 (render
reads structure + label()/params()/output() only, never eval), C8 (sink =
zero-output leaf, no special case), C12 (blueprint = root composite is
render-structural only; types stay distinct), C23 (consumer-prefix mirrors
the := binding), #38 (composites stay opaque) — all hold. Three fidelity
items, resolved:

- [high, fixed] spec acceptance over-claimed: the SimBroker slot-stub box read
  as in-scope though the work is deferred. Spec amended — the stub acceptance
  is annotated against #49, the met boxes checked.
- [medium, recorded] no docs/plans/0023: this cycle ran spike→refactor under
  user steering instead of planner→implement, because the edge-label-vs-
  consumer-side fork needed an empirical probe (ascii-dag silently drops edge
  labels on collision) before a placeholder-free plan was possible. A plan
  RECORD (not a task plan) now holds the 0023 slot so the counter pairing
  stays intact and the next cycle takes 0024.
- [low, carry-on] multi_output_field_name's leaf-producer fallback is latent
  and untested (no multi-output leaf in the corpus). Carried as debt.

Regression scripts: none configured (no-op). Build/test/clippy green.
Deferred stub work is tracked in #49 (user-signed).

closes #48
2026-06-09 00:15:50 +02:00
Brummel 5dd9503e40 spec: 0023 unify blueprint main-graph render
The CLI blueprint main-graph render is a separate, poorer path than the
composite-definition render: bare leaves, bare edges, dropped from_field.
Unify both through the composite machinery — the blueprint is the root
composite, differing only at the borders (sources, factory params, sinks
as zero-output terminal leaves). Harness signature header deferred.

refs #48
2026-06-08 23:04:38 +02:00
Brummel bb90c42028 audit: cycle 0022 (#46) — drift reconciled; output bindings shipped
Architect drift review (range 69d2094..HEAD), regression gate empty
(no scripts configured). Status: drift_found, all resolved here or queued.

What holds: C8/C23 intact — output_binding folds OutField.name onto the
producer label as a pure render symbol; compiled_view_golden byte-identical,
no name reaches the compilat; the drawn where: graph is now exactly the
computation DAG (false terminals + node-count inflation gone).

Resolved (doc reconciliation, this commit):
- docs/design/INDEX.md: two stale render descriptions brought current —
  the C9/0018 realization said output names render as `[out:<name>]`
  markers; the C19/0017 realization said `[in:k]`/`[out]` port markers.
  Both superseded by the `name := producer` binding form (the `[out]`
  staleness is cycle-0022's; the adjacent `[in:k]` was pre-existing drift
  from 0019/0020 and is reconciled in the same clause while here).
- docs/specs/0022: post-ship note added — its Components/Testing strategy
  under-counted the blast radius, missing the full-render golden
  blueprint_view_golden (forced re-capture, caught at implement).

Queued (not fixed): output_binding's tuple arm `(a, b) := …` is spec'd +
shipped but unexercised (no fixture re-exports >1 field from one node) —
filed as #47 (idea).

cycle 0022 drift-clean after this reconciliation (not a milestone close).
2026-06-08 17:20:18 +02:00
Brummel b2f27f8590 plan: 0022 composite output binding render
RED-first: re-pin all five render assertions whose producer carries an
OutField (macd, sma_cross cross, two fan-in `o` outputs — across main.rs
+ cli_run.rs) to the `:= ` binding form, then fold output_binding onto
the producer label in render_definition. Blast radius wider than spec
§Components named (flagged for audit).

refs #46
2026-06-08 17:10:54 +02:00
Brummel 7d4740e8b2 spec: 0022 composite output binding render
Render composite output re-exports as producer-label bindings
(`name := <producer-label>`) instead of standalone terminal nodes:
folds the output name onto its producing node, dropping the false
terminals and the node-count inflation (MACD where: 8 -> 6). Pure
render layer; C8/C23 untouched, compiled view byte-identical.

refs #46
2026-06-08 17:04:16 +02:00
Brummel 3f4d756ed2 feat(aura): fan-in input distinguishability — construction constraint + source-derived render
A fan-in node (>1 input) whose colliding sources hide an unnamed configuration
axis is now illegal at construction, and the definition view renders each fan-in
input as a source-derived recursive-signature identifier instead of the
positional, meaningless #A/#B.

The root defect was sma_cross: two Sma into a Sub with unaliased length params,
rendering sma_cross() -> (cross) with two indistinguishable [SMA] and
[Sub(#A,#B)] — the author meant fast vs slow but the identity hid it. Now it
renders sma_cross(fast:i64, slow:i64) with [SMA(fast)]/[SMA(slow)] and
[Sub(#Sf,#Ss)].

Shape (single source of truth for "signature" shared by engine + CLI):
- aura-engine signature_of(): a node's recursive authoring identity — type
  initial + alias initials + each wired input's signature (recursing into
  interior leaves, stopping at named roles/composites at their initial).
- aura-cli leaf_label/fan_in_identifiers: shortest sibling-unique prefix of a
  source's signature, never below its base (type + alias initials); a role-fed
  slot renders its name verbatim (#price); equal-signature interchangeable
  inputs keep the positional letter.
- aura-engine IndistinguishableFanIn: a signature collision is a fault only when
  a colliding source carries an unaliased param (the unnamed axis); param-less
  interchangeable sources (fan_composite's Pass/Pass) stay legal.

Param-aware criterion (refined during design): keeps the blast radius to the
param-bearing alias-less fixtures (sma_cross CLI + engine, the nested fast_slow);
fan_composite and the hand-wired flat fixtures stay green.

Placement decision (deviates from the plan): the constraint runs as a structural
pre-pass (check_fan_in_distinguishability) at the head of compile_with_params,
BEFORE the param-arity gate — not inside inline_composite as the plan drafted.
Reason: the no-param compile() of a param-bearing composite would hit ParamArity
first and preempt the fault; the spec mandates a structural check needing no
param values, so the pre-pass is the spec-aligned home. Alias-validity ordering
(BadInteriorIndex before the fan-in fault) is preserved at the pre-pass head.

C23 untouched: the check is construction-phase; the compilat stays name-free
(compiled_view_golden byte-stable). Ledger C9 carries the refinement.

Known debt (non-gating): the alias-index validity check now exists in both
inline_composite and the pre-pass (the pre-pass reaches every composite first,
making inline_composite's copy effectively dead). Left for a separate tidy — a
5-line, correctness-neutral removal with its own test surface.

closes #44
2026-06-08 15:50:19 +02:00
Brummel 3aa124f815 plan: 0021 fan-in distinguishability
Four tasks: shared recursive signature_of helper (aura-engine); CLI
source-derived fan-in identifiers + sma_cross aliases + render goldens
(aura-cli); the IndistinguishableFanIn construction constraint + engine fixture
aliases (aura-engine); the C9 ledger refinement. Ordered so the workspace test
suite stays green at every task boundary.

Refs #44
2026-06-08 15:35:57 +02:00
Brummel 22a690361a spec: 0021 refine fan-in constraint to a param-aware criterion
A signature collision is a fault only when at least one colliding source
carries an unaliased param slot — the configuration axis the author left
unnamed (sma_cross's length). Equal-signature sources with no param are
genuinely interchangeable (fan_composite's Pass/Pass, add(price,price)) and
stay legal; the render uses the positional letter for them. Keeps the
construction blast radius to the param-bearing alias-less fixtures.

Refs #44
2026-06-08 15:21:51 +02:00
Brummel 14d91035ec spec: 0021 fan-in distinguishability
A construction-phase constraint (CompileError::IndistinguishableFanIn, raised in
inline_composite) makes a fan-in node whose sources have identical recursive
signatures illegal; the definition view then renders each fan-in input as the
shortest unique prefix of its source's signature (replacing positional #A/#B);
and the sma_cross fixture gains its missing fast/slow aliases.

Refs #44 — scope extended from render-only to a construction-phase invariant
during design (the positional #A was a symptom of unnamed, indistinguishable
fan-in sources, which is the actual fault to forbid).
2026-06-08 14:08:59 +02:00
Brummel 71f50330db plan: 0020 composite signature render
Two-task render-only plan executing spec 0020:

- T1 (graph.rs + node.rs): add `signature` (typed title line, with a new
  ScalarKind->lowercase `kind_str` match — none exists today, {:?} gives
  PascalCase) and `leaf_label` (fold aliased param names + ordered
  input-slot stubs); edit render_definition (title->signature,
  leaf->leaf_label, drop [param:*] loop, de-prefix outputs); fix two stale
  doc comments (the render_definition [param:] description + the
  node.rs:93-101 cluster rationale). Gate: cargo build -p aura-cli (the
  goldens go red until T2 re-captures; cargo test is NOT a T1 gate).
- T2 (main.rs): repin the FIVE title asserts name:->name( (the blast
  radius: sma_cross/outer/inner/dup/macd, four beyond the spec's named
  one); update the definition needle ([Sub]->[Sub(#A,#B)], [out:cross]->
  [cross]); rewrite the MACD render assertions (drop [param:*], add the
  signature + [EMA(fast)] + [Sub(#A,#B)] + [macd] + [param:/[out: absence);
  re-capture blueprint_view_golden wholesale; confirm compiled_view_golden
  byte-identical (C23); full --workspace triple -D warnings.

Render-only: no engine/ParamAlias/param_space change; compiled_view_golden
byte-identical; aura run --macd unchanged. Output kinds deferred to #43.

refs #41 #43
2026-06-08 10:27:38 +02:00
Brummel 42eb62771d spec: 0020 composite signature render
Render-only refinement of #41's composite blueprint-definition view. The
[param:<name>] marker nodes #41 added bloat the ASCII graph; this moves the
interface into a typed signature title line and folds names into the node
labels that already exist:

- title: macd(fast:i64, slow:i64, signal:i64) -> (macd, signal, histogram)
  (param kinds from factory().params()[slot].kind; OUTPUT NAMES ONLY)
- leaf labels fold aliased param names: [EMA] -> [EMA(fast)]
- unnamed interior input slots as positional stubs: [Sub] -> [Sub(#A,#B)]
  (count/order derived render-side from c.edges() + c.input_roles())
- outputs de-prefixed: [out:macd] -> [macd]; [param:*] marker nodes removed
- fix stale node.rs:93-101 cluster rationale (renderer went flat in 0017)

Output KINDS are deferred: a value-empty LeafFactory exposes no output
schema pre-build (the mc_4/#42 finding), so the typed arrow (-> macd:f64)
waits on #43. Render-only: no engine/ParamAlias/param_space change;
compiled_view_golden byte-identical (C23); aura run --macd unchanged.

Gates green: feature-acceptance (the worked MACD before->after is the
evidence), placeholder scan clean, parse-every-block no-op (no
spec_validation parser), grounding-check PASS (10 assumptions ratified;
both load-bearing premises — no pre-build factory output schema, flat
renderer — confirmed).

refs #41 #43
2026-06-08 10:19:42 +02:00
Brummel ebe2e71349 audit: cycle 0019 (#41) — drift-clean; C9 boundary-uniform realization note
Architect drift review over 8bc429f..HEAD (spec 03f2dc4, plan c9115e3,
impl 41cbb55; plus the in-range #42 fieldtest port 241bb62). No regression
scripts configured (profile regression/architect_sweeps both empty) — the
architect is the sole gate.

Result: clean. C23 holds — Role.name / ParamAlias.name / OutField.name are
all dropped at lowering; compiled_view_golden byte-identical (untouched in
the diff); the param_space slot-order anchors green and unmodified. Param
aliasing is a pure naming overlay (relabel in place, slot order/arity/kind
unchanged), confirmed by the new relabel/unaliased/partial tests. C8/C7/C4/
C9 untouched (authoring-surface legibility only); BadInteriorIndex reused,
no new variant. The #42 port is faithful (assertions strengthened, not
gutted).

One drift item, resolved here: the 0018 C9 realization note carried a stale
"Input-role and param names ... remain index-only pending #41" sentence.
#41 has landed, so this commit replaces it with a cycle-0019 realization
note recording that all three composite-boundary edge-kinds (input roles /
params / outputs) are now uniform named projections of interior handles
(Role / ParamAlias / OutField), names non-load-bearing and dropped at
lowering — the prose now matches the shipped code.

drift-clean, not a milestone close (no milestone fieldtest run here).
2026-06-08 02:24:15 +02:00
Brummel c9115e3e0f plan: 0019 name composite boundary
Four-task plan executing spec 0019, sequenced like cycle 0018 (type-shape
change rippling engine -> CLI):

- T1 engine type migration, behaviour-preserving: Role/ParamAlias types,
  Composite struct/new/accessors, inline_composite role-walk, lib.rs
  re-export, all engine test Composite::new sites. Gate per-crate
  (cargo test -p aura-engine, NOT --workspace; aura-cli compiles in T3).
  The new params field is dormant -> param_space byte-identical, C23
  anchor goldens green.
- T2 param aliasing RED-first: collect_params alias relabel + inline_composite
  alias range-validation (-> BadInteriorIndex, reusing the variant), with
  4 new unit tests (relabel, out-of-range, unaliased regression, partial).
- T3 CLI: render_definition [in:name]/[param:name] markers; MACD author
  site (named role + fast/slow/signal aliases, the worked example);
  sma_cross named role only; blueprint_view_golden re-capture;
  compiled_view_golden stays byte-identical (C23 guard).
- T4 fieldtests sweep (separate workspace root -> explicit cargo build
  gate, guarding the #42 latent-drift recurrence) + full --workspace
  triple (-D warnings).

Aliasing demonstrated on the CLI MACD site only (the spec's evidence);
every other composite site gets the forced role-name + empty params, so
the param_space C23 anchor goldens stay byte-identical.

refs #41
2026-06-08 02:08:57 +02:00
Brummel 03f2dc4c51 spec: 0019 name composite boundary
Sibling of #40 (composite output as a named multi-field record). Brings
the other two composite-boundary edge-kinds to the same named-projection
shape: input roles (Vec<Role { name, targets }>, rendered [in:<name>])
and a param-alias overlay (Vec<ParamAlias { name, node, slot }>) that
relabels an interior leaf param slot's surface name in param_space().

Load-bearing decision recorded in the spec: param aliasing is a pure
naming overlay, NOT curation — every interior slot stays in param_space()
and sweepable; the alias only relabels in place, never reorders/hides
(forced by the issue's "legibility not capability" framing + C23). The
injected point vector is unchanged; MACD surfaces macd.fast/slow/signal
instead of three indistinguishable macd.length.

Gates green: feature-acceptance (worked MACD before->after is the
evidence), placeholder scan clean, parse-every-block no-op (profile
declares no spec_validation), grounding-check PASS (7 load-bearing
assumptions each ratified by a named currently-green test; compiled_view
+ param_space slot-order goldens stand as the C23 regression anchors).

refs #41
2026-06-08 01:58:44 +02:00
Brummel 8bc429f367 audit: cycle 0018 (#40) — drift-clean; C9 multi-output realization note
Architect drift review over a4cfe5c..HEAD (covers the previously-unaudited
EMA node and MACD PoC plus the #40 composite multi-output cycle): status
clean. C8/C7/C4 preserved (one port, one row per eval, K co-fresh base
columns — structurally identical to OHLCV); C23 honoured (ItemLowering::
Composite.output is Vec<(usize,usize)>, no name in the compilat; the
compiled-view render stayed bit-identical). EMA and MACD PoC carry no
ledger drift.

Resolution of the three minors:
- Ledger lagged the new behaviour: added a "Realization (cycle 0018)" note
  under C9 recording that a composite's output is now a named K-field record
  (Vec<OutField>), the same arity C8 already grants a leaf, names dropped at
  lowering (C23). Amended here (ledger amendment is the audit's job).
- blueprint.rs module-doc / Composite struct-doc / new-doc still said "one
  output port" singular — updated to "output record".
- Non-workspace fieldtest From<Sma> debt is pre-existing (cycle-0016) and
  already tracked as #42; not re-filed.

No regression scripts configured (profile regression: []), so the architect
review is the sole gate. Carry-on: the next natural iteration is the #41
legibility sibling (input-role + param naming at the composite boundary).
2026-06-08 01:05:41 +02:00
Brummel 2c33a8d74f plan: 0018 composite multi-output record
Four tasks: engine type migration (behaviour-preserving, caps kept) →
cap-lift with RED-first capability tests → CLI render + author sites +
golden re-capture → non-workspace fieldtest sweep + workspace gate.

refs #40
2026-06-08 00:47:31 +02:00
Brummel 9ddeab8b7e spec: 0018 composite multi-output record
Composite::output: OutPort -> Vec<OutField { node, field, name }> so
multi-line indicators (MACD/Bollinger/Stochastic/Ichimoku) can be
authored as a composition and re-exported as a named record. Output
half only; param/input-role naming is the dependent sibling.

refs #40
2026-06-08 00:37:37 +02:00
Brummel a4cfe5c53f audit: cycle 0017 (#38) — render_flat dedup + ledger #38 realization note
Architect drift review of the #38 cycle (a70c5fa..cd31447) against the design
ledger. No ledger-contract drift: C23 holds (render_compilat byte-identical,
compiled_view_golden green), C9/C12 honoured (the new main-graph + where:-
definitions view renders a composite as an opaque authoring-level node with its
body defined once — a faithful blueprint/source view vs. the inlined compiled
form), the nested-composite unimplemented! is genuinely removed and tested, and no
orphaned references to the removed ItemDisplay/producer_id/consumer_ids remain in
crates/ (only frozen historical plan docs mention them).

Two debt items, both fixed here (not carried):

- [medium] crates/aura-cli/src/graph.rs — render_compilat open-coded the same
  Graph::with_mode/add_node/add_edge/render idiom the new render_flat helper wraps.
  Fixed: render_compilat now builds its edge-pairs (its Edge structs + source
  targets) and delegates to render_flat, so render_flat is the single flat-build
  point shared by all three render functions. Behaviour-preserving — same node and
  edge insertion order — verified by compiled_view_golden staying byte-identical.

- [low] the retired cluster-box blueprint-render model and its substantive cause
  (ascii-dag 0.9.1's subgraph /2 centering bug; flat-only being the fix) lived only
  in the superseded spec 0013, not in the durable contract layer. Fixed: added a
  "Realization (cycle 0017 — blueprint render = main graph + definitions)" note to
  the ledger (INDEX.md, under the C22 blueprint-view detail) recording the
  opaque-node + where:-definitions model, the authoring-vs-compiled split, the
  ascii-dag-bug rationale, the scale/render-once/nested benefits, and the #37
  playground (enter/focus) counterpart vs. #38 static CLI form.

regression: profile declares no regression scripts and no architect_sweeps — the
architect review is the sole gate. No baseline to update.

Cycle 0017 is drift-clean after these fixes. NOT a milestone close — that needs the
end-to-end milestone fieldtest, still deferred until the sweep root (#32-#33) lands.
2026-06-07 22:29:42 +02:00
Brummel 870dfcec5b plan: 0017 blueprint view as main graph + composite definitions
Two tasks, CLI-render-only (graph.rs + main.rs tests):
- Task 1: rewrite render_blueprint to the main-graph + where:-definitions model
  (composite = opaque node; collect_distinct_composites recursive/deduped by name;
  render_definition with [in:k]/[out] port markers); drop ItemDisplay/producer_id/
  consumer_ids and the nested-composite unimplemented!; behavioural tests RED->GREEN
  (opaque-node, defines-once, nested-no-panic, reuse-defined-once).
- Task 2: recapture the blueprint golden from `aura graph` stdout; full-workspace
  test + clippy gate. render_compilat untouched (its golden stays byte-identical).

refs #38
2026-06-07 22:20:28 +02:00
Brummel 2d3792e45f spec: 0017 blueprint view as main graph + composite definitions
Redesign the `aura graph` blueprint view (render_blueprint) from ascii-dag
subgraph cluster boxes to a "program with subroutines" model: a small main
graph that wires the harness with each composite shown as a single opaque
node, plus a `where:` section rendering each distinct composite type's
interior once. render_compilat (flat, fully-inlined, C23) is unchanged.

Design ratified in-session. The 3a-vs-3b fork was resolved by medium: the
interactive enter/focus model (3a) is the playground's, parked as #37; the
static main+definitions model (3b) is the CLI's, this cycle.

Substance:
- Correctness under width: ascii-dag 0.9.1's subgraph level-centering rounds
  sibling x-positions with `/2`, overlapping wide sibling labels (a
  width/parity-sensitive bug with no config/padding dodge that survives
  unequal-width siblings). The flat layout is collision-free; the new model
  renders only flat graphs, so the bug cannot arise.
- Scale: a composite is one opaque node in the main graph, so blueprint size
  tracks top-level wiring, not inlined node count — real strategies stay
  displayable.
- Render-once: a composite reused N times has its body rendered once.
- Removes the nested-composite `unimplemented!` at graph.rs:74-77 (the
  definitions pass is recursive).

Defaults baked (not forks): composite-type dedup key = name() (assumes
same name => same structure, the authoring convention); opaque-node ports
shown as [in:k] / [out] markers in a definition.

grounding-check PASS — all nine load-bearing assumptions ratified by green
tests (Composite/Blueprint API, the flat render_compilat path, the
unimplemented! site, the two replaced cluster tests, the four must-stay-green
tests). spec_validation parse gate is a confirmed no-op (none configured).

refs #38
2026-06-07 22:13:15 +02:00
Brummel a70c5faab7 audit: cycle B (0016) — ledger 0016 realization note (C8/C19), debt #36 filed
Architect drift review of the #31 cycle (2a9a141..4b64409) against the design
ledger. The feature is clean: C19/C23 realized literally (LeafFactory recipe;
compile_with_params builds-then-wires slot-by-slot in the same walk param_space()
projects — #34's dual-traversal hazard genuinely subsumed, one traversal, both
mirror tests green incl. nesting); C12 "no recompile per param-set" holds
(value-empty blueprint, vector-bound, bit-identity green); the removed
Composite::schema/BlueprintNode::schema confirmed caller-free.

ledger (fixed here):
- C8: the 0015 note's "binding a value to a slot (#31) ... deferred" line was stale
  (shipped). Updated to record #31 landed in 0016, and that the param declaration's
  authoring home moved to LeafFactory.params() (read pre-build) alongside the built
  node's schema().params (the duplication is debt #36).
- C19: added a "Realization (cycle 0016 — param-set injection)" note (the per-cycle
  pattern C8/C10 already carry for 0005/0006/0007/0015): the value-empty leaf,
  build-then-wire bootstrap_with_params/compile_with_params, the typed kind/arity
  errors, the bit-identical compilat, and the C22 blueprint-view bare-type label
  (value-bearing SMA(2) now only in the compiled view).

debt (filed, carry-on):
- Every param-bearing node declares its ParamSpecs twice (factory() and schema()),
  kept in lockstep only by a per-node test; drift compiles. Filed as #36 (sibling of
  #34's traversal-duplication concern). Not active drift — the agreement holds and
  is tested per node.

regression: profile declares no regression scripts and no architect_sweeps — the
architect review is the sole gate. No baseline to update.

Cycle B is drift-clean after these amendments. NOT a milestone close — that needs
the end-to-end milestone fieldtest, deferred until the sweep root (#32-#33) lands.
2026-06-07 21:08:49 +02:00