03285d1b47682b93eb2f12e7cc9face3563158ea
180 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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
|
||
|
|
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 |
||
|
|
64b19ab3d5 |
feat(aura-engine,aura-cli): named param binding — single-run .with()/.bootstrap() (0030 iter 1)
Bind a blueprint's open knobs by name for a single run instead of by a positional,
Scalar-wrapped Vec in param_space() order:
bp.with("sma_cross.fast", 2).with("sma_cross.slow", 4).with("scale", 0.5).bootstrap()
A pure authoring layer over the existing Composite::param_space / bootstrap_with_params
primitives; the engine run loop and the construction core are untouched (C1/C12/C19/C23
preserved). Raw literals lower via Into<Scalar> (the literal fixes the variant: 2->I64,
0.5->F64, pinned by
|
||
|
|
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 |
||
|
|
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
|
||
|
|
e97906a0e1 |
test(aura-core): pin bare-literal Into<Scalar> kind inference
Authoring layers that lower raw literals via `impl Into<Scalar>` (the named-param-binding surface, spec 0030 / #35) rely on a bare integer literal inferring as i64 and a bare float as f64 — because i64/f64/bool are the only `From<…> for Scalar` impls, each literal class resolves to exactly one. This was true but untested: `from_widenings` uses suffixed literals (`7i64`), so a regression in the inference path would survive it. Behaviour-preserving pin of existing aura-core behaviour. Closes the one grounding gap the grounding-check flagged on spec 0030; also guards the property independently — adding a second integer `From` impl would now break loudly here rather than silently at a `.with("knob", 2)` call site. refs #35 |
||
|
|
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 |
||
|
|
5bded0ca83 |
feat(aura-cli): aura runs registry — persist on sweep, list + rank (cycle D / iter 3)
Final iteration of the run-registry cycle (#33): the read surface that makes a sweep family — and runs across separate invocations — comparable over time (C18's "compare experiments over time", which has no home in git or Gitea). - `aura sweep` now persists each point's RunReport to runs/runs.jsonl (append- only, one serde_json line per run) in addition to printing it. - `aura runs list` prints every stored record, in store order. - `aura runs rank <metric>` prints the stored runs best-first by the metric (total_pips desc; max_drawdown / exposure_sign_flips asc); an unknown metric is a usage error (exit 2), like every other aura arg error. sweep_report splits into a production sweep_family() (the pure run) and a #[cfg(test)] renderer kept for the in-bin goldens; the dispatch gains run_sweep / runs_list / runs_rank over default_registry() (runs/runs.jsonl under cwd). Process goldens run the binary in a temp cwd so persistence never dirties the repo; /runs/ is git-ignored as a backstop. Verified end-to-end: two `aura sweep` invocations accumulate 8 records; `aura runs list` shows all 8; `aura runs rank total_pips` orders them best-first; `aura runs rank bogus` exits 2 with the known-metrics hint. cargo test --workspace green (cli_run 11, registry 5, engine 93, …); clippy --workspace --all-targets -D warnings clean; no stray runs/ in the work tree. refs #33 |
||
|
|
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 |
||
|
|
fe11cfea8a |
feat(engine,registry): SweepPoint carries RunReport; add aura-registry (cycle D / iter 2)
Iteration 2 of the run-registry cycle (#33): make the sweep family self-describing and add the registry store. Engine. SweepPoint.metrics (RunMetrics) becomes SweepPoint.report (RunReport), and the sweep closure bound is now Fn(&[Scalar]) -> RunReport. This closes the manifest-per-point gap cycle C explicitly deferred to #33 — each swept point now carries its full (manifest, metrics), the unit the registry indexes (C18). All engine-internal callers, the engine test fixture run_point, and the three sweep tests were rethreaded in the same iteration so the crate stays green; the CLI caller followed in the same change so `cargo test --workspace` is green again. CLI. The sweep_report closure builds a per-point RunReport (manifest params = param_space names zipped onto the point via scalar_as_param_f64; commit/window/ seed/broker as run_sample builds them) and prints via RunReport::to_json. The hand-rolled sweep_point_to_json/json_string/scalar_token are retired (the report renders itself), and the orphaned ParamSpec/SweepPoint imports dropped. Net: aura run, aura sweep, and (next iteration) aura runs all print the same RunReport shape. The two aura sweep goldens now pin the per-point manifest params, NOT the commit (it is the real git HEAD, volatile). Registry. New crate aura-registry: Registry::{open, append, load} over an append-only runs/runs.jsonl (one serde_json line per RunReport), free rank_by (best-first per metric — total_pips desc, max_drawdown/exposure_sign_flips asc), and RegistryError (Io / Parse{line} / UnknownMetric). Five unit tests cover append->load round-trip, missing-file = empty, corrupt-line = Parse{line}, per-metric ranking, and unknown-metric error. Built and unit-tested; NOT yet CLI-wired (aura sweep persistence + aura runs list/rank are iteration 3). Verified: cargo test --workspace green (aura-registry 5, aura-engine 93, aura-cli 7+8, others unchanged); clippy --workspace --all-targets -D warnings clean; aura run stdout shape unchanged; aura sweep now emits full RunReports. refs #33 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
728ca1a2d2 |
chore(boss): enable spec auto-sign for this project
The orchestrator may now sign a spec in the user's place under /boss, but only through specify's Step-6 auto-sign gate: all objective gates green (precondition, parse, grounding-check PASS) AND a unanimous five-lens spec-skeptic panel. The human signature stays mandatory on any non-PASS path. |
||
|
|
16e31fe05d |
audit: cycle 0028 (#32) — drift-clean; param-sweep grid (the World, cycle C)
Architect drift review over 776bd54..HEAD: drift-clean on every load-bearing contract. - C1: sweep()'s disjointness is real — sweep_with_threads shares only an AtomicUsize cursor, tags results by point index, sorts post-join; the family order is the enumeration order, thread-count-independent (pinned by family_is_deterministic_across_thread_counts at 1 vs 8 vs derived-N). Each point builds fresh, shares nothing mutable. First realization of "parallelism across sims, never within one". - C16: zero-external-dependency intact — crates/aura-engine/Cargo.toml unchanged, no dependency added; execution is std::thread::scope, never rayon. No serde. - C12: scope discipline held — only the C12.1 grid axis landed; random / optimize / walk-forward / MC and per-point manifest assembly are absent and deferred per spec. - C19/C20: untouched — sweep drives bootstrap_with_params per point, no topology mutation. - C14: the CLI's sweep_point_to_json mirrors RunReport::to_json's hand-rolled token rules byte-for-byte; no second JSON dialect. C8/C18: metrics-drain stays author-side in the closure (the engine ↔ harness boundary the design intends). Regression: no scripts configured (profile regression: []) — architect is the sole gate, and it is green. Debt (filed, blocks nothing): - #53 — the SMA-cross sample harness topology is now duplicated across three sites (blueprint.rs test, sweep.rs test, cli main.rs production) with no test pinning them equal. Pure tidy, carry-on. Deferred follow-up filed: - #52 — random param-sweep enumeration (C12.1's other half: ranges + sample-count + seeded RNG), the deliberate scope cut from this cycle's design. Cycle 0028 closes #32 (param-sweep grid, engine primitive + aura sweep CLI demonstrator). Milestone "parameter-space & sweep" is NOT closed by this audit (that needs the milestone fieldtest, a separate manual act); #33 (run registry, cycle D) remains open in the milestone. closes #32 |
||
|
|
e94ee4253a |
feat(aura-cli): aura sweep — grid-sweep demonstrator (the World, cycle C / iter 2)
Make the iteration-1 sweep primitive visible without writing Rust: an
`aura sweep` subcommand runs the built-in sample over a small built-in grid
(fast in {2,3}, slow in {4,5}, scale in {0.5} = 4 points) and prints one
canonical JSON line per point, in enumeration (odometer) order.
- sample_blueprint_with_sinks() -> (Composite, Receiver, Receiver) now holds
the sample topology and returns the two Recorder receivers a per-point run
drains; build_sample() (the `aura graph` entry, which never runs the graph)
is re-expressed as `sample_blueprint_with_sinks().0`, so there is one
topology definition and the graph render is unchanged (its golden stays
green).
- sweep_report() declares the grid over the sample's param_space and calls the
engine sweep() with a per-point closure (build fresh -> bootstrap_with_params
-> run -> drain both sinks via f64_field -> summarize). Pure + deterministic
(C1): the same build yields a bit-identical report.
- sweep_point_to_json + json_string + scalar_token hand-roll the JSON (C14, no
serde — the engine's json_str is private), mirroring RunReport::to_json's
token rules: a whole-valued f64 renders without a fractional part (12.0->12),
an I64 as an integer token, an F64 as the shortest round-trippable form.
- The main() dispatch grows a ["sweep"] arm; USAGE advertises it; the strict
trailing-token contract is inherited (aura sweep extra -> exit 2).
Tested: sweep_point_to_json byte-pinned on a hand-built point (the f64/i64
token rules); sweep_report pins 4 lines with per-line params byte-exact in
odometer order + determinism; process goldens in tests/cli_run.rs pin the
stdout line-count/exit-zero and the strict-arg exit-2 path. Full
cargo test --workspace green (graph golden, engine iter-1 sweep tests, run/macd
all unaffected); clippy --workspace --all-targets -- -D warnings clean.
refs #32
|
||
|
|
7bfcc3b86e |
plan: 0028 sweep CLI demonstrator (iteration 2)
refs #32 |
||
|
|
a665a966a7 |
feat(aura-engine): param-sweep grid primitive (the World, cycle C / iter 1)
Turn one value-empty blueprint into a family of disjoint instances over a
declared grid, run them in parallel, and collect per-point metrics. This is
C12.1's grid axis — the first "N instead of 1": #30/#31 made a blueprint
param-generic and bindable; this reaps it.
New module crates/aura-engine/src/sweep.rs, three parts:
- GridSpace: a validated cartesian grid over a blueprint's param_space() —
one discrete value-list per slot, kind/arity/non-empty checked at
construction (SweepError::{Arity, KindMismatch, EmptyAxis}, the typed gate
before any run). points() enumerates in odometer order (last axis fastest),
deterministic.
- sweep(): closure-driven. The engine owns enumeration + disjoint execution
+ collection; the author owns run_one: Fn(&[Scalar]) -> RunMetrics (build
fresh -> bootstrap_with_params -> run -> drain -> summarize). Metrics
reduction is harness-specific sink glue the engine cannot generically own
(C8/C18), and a fresh per-point build resolves #31's two structural facts:
bootstrap_with_params consumes the blueprint, and a Recorder bakes its
channel in — so each point gets its own drainable instance.
- SweepFamily / SweepPoint: ordered, self-describing (params carry the point
coordinate), in enumeration order independent of thread completion.
Execution is std::thread::scope over available_parallelism() workers pulling
point indices from a shared atomic cursor (work-stealing balances uneven
per-point cost); results are tagged by index and sorted after join, so the
family is bit-identical across thread counts and repeated runs (C1: disjoint,
lock-free; order = enumeration, not completion). No external dependency —
std only, never rayon (C16: the engine workspace stays zero-dep for deploy
purity). A private sweep_with_threads(nthreads) lets the tests pin
determinism at 1 vs N workers while the public sweep() derives the count.
Out of scope this iteration: the `aura sweep` CLI demonstrator (cycle 0028
iter 2); random enumeration, per-point manifest assembly (#33), value-domain
validation (C20) are deferred per the spec.
One deviation from the plan's verbatim code: GridSpace derives Debug
(load-bearing — the three .unwrap_err() fault tests need the Ok type to be
Debug to compile).
Verified: 8 new sweep:: tests green (enumeration/odometer order, the three
typed faults, sweep == N independent runs, determinism across thread counts,
distinct-points-distinct-metrics); cargo test --workspace green (92 engine
lib tests, no regression); cargo clippy --workspace --all-targets
-- -D warnings clean.
refs #32
|
||
|
|
e2b6ab8e1b |
plan: 0028 param-sweep grid (iteration 1 — engine primitive)
refs #32 |
||
|
|
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 |
||
|
|
776bd5432a |
fix(aura-cli): emit valid DOT identifiers for inline-expanded composites
Observed bug in the graph viewer shipped in
|
||
|
|
53f3fcf198 |
audit: cycle 0026 (#51) — drift-clean; aura graph is a self-contained WASM-Graphviz viewer
Cycle-close audit for cycle 0026, iteration 2 (the graph render viewer). Scope 7569980..HEAD (plan |
||
|
|
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 |
||
|
|
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 |
||
|
|
7569980dcd |
audit: cycle 0027 (#50) — drift-clean; input ports named
Architect drift review (288f589..e304dba): cycle 0027 introduced NO drift, no contract weakened, no ledger edit beyond the cycle's own C8 realization note. - C23 (identity positional, names non-load-bearing): held. PortSpec.name is read only by port_json / scope_json (render); derive_signature carries Role.name for display; wiring stays by index (Edge/Target), the bootstrap kind-check reads .kind, never .name. The compilat is untouched. - C9 (read-only render): model_to_json still &Composite -> String, no eval/build/bootstrap/compile on the path. - C14 (no serde): still hand-rolled format! JSON. - C4 (four scalar base types): unchanged. - Homogeneity goal met: the re-captured model golden names every input tuple at both leaf and composite level, matching outputs/params; nothing half-named. - Verification (re-run by the orchestrator, not taken on the agent's word): cargo build --workspace clean; cargo test --workspace 168 green / 0 red; clippy --all-targets -D warnings clean. Regression gate: profile configures no regression scripts (regression: []) — a no-op this cycle; architect is the sole gate and it is clear. Deferred (pre-existing debt, NOT 0027 drift): graph.rs:324 multi_output_field_name carries a stale #43 comment ("leaf field names unavailable pre-build") — false since cycle 0024; the leaf branch falls back to the field index. The file is the ascii-dag renderer slated for retirement by #51 (0026 it2); the dead fallback dies with the file there. Recorded as a comment on #51 so it is not lost — not a tidy iteration, since fixing soon-to-be-deleted code is wasted effort. #21 intentionally NOT closed (a swapped same-kind wiring is still only kind-checked) — the cycle's stated scope boundary, not drift. |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
288f58953e |
feat(aura-engine): graph model serializer (0026 it1)
Iteration 1 of the graph render redesign: a read-only model_to_json(&Composite) that serializes the harness root + every distinct composite type into the canonical, deterministic JSON model the browser viewer will consume. New crates/aura-engine/src/graph_model.rs; lib.rs re-exports model_to_json. - House style: hand-rolled JSON like RunReport::to_json, NO serde (C14). - Model shape (faithful to the types, resolving the spec's prototype-flavoured example): node keys are indices (C23, the blueprint has no unique names); input ports carry kind+firing only, no name (PortSpec has none — acceptance criterion 3); a vec![] output is the C8 sink; bound root source-roles are minted as synthetic src_<role> source nodes; composite boundaries use @role (input) and #N (output) endpoints; distinct composites collected once. - Improvement over the plan: a composite's input kind is read from the actually fed interior slot (matching blueprint::derive_signature) rather than a hardcoded f64 — invents nothing. - Read-only (C9): &Composite -> String, no eval/compile/bootstrap on the path. - 10 new tests green: byte golden on the sample harness, determinism, structural mis-wire differs (param swaps are invisible to the pre-compile model — the property is carried by a structural re-target), read-only witness, sink / two-input / distinct-once structural assertions. Workspace green, clippy clean. aura graph still renders via ascii-dag (untouched); iteration 2 ports the viewer JS, vendors the WASM-Graphviz asset, emits the self-contained HTML, and retires ascii-dag. |
||
|
|
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. |
||
|
|
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. |
||
|
|
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. |
||
|
|
63af2d1095 |
audit: cycle 0025 (#49) — drift-clean; root renders like any composite
Architect drift review (d6f59d7..12b1bce): NO code drift, no contract weakened,
no ledger edit needed.
- C9 (read-only render): the change touches only label/entry assembly
(render_blueprint, leaf_label, render_graph signature); no eval, no run-path
code. 150 tests green (same count), clippy clean, build green.
- C12 ("mis-wiring visible before a run"): realized at the root, not overstated —
a top-level fan-in's wiring was invisible (bare [SimBroker]); it now surfaces
which producer feeds which slot ([SimBroker(#E,#price)]). The C8 label-render
refinement (INDEX.md:250, "label() surfaces a mis-wiring") is extended to the
root, not contradicted.
- C23 (names dissolve post-inline): render_compilat byte-untouched (still emits
source:{kind} from SourceSpec.kind); the [price] pre-inline / [source:F64]
post-inline split now renders C23's boundary-dissolution visibly, with
compiled_view_golden as the negative control.
- C3 (sources bind at ingestion only): root entries filtered to source.is_some()
bound roles — the one semantic root-vs-interior distinction, correctly retained.
- #49 acceptance: the Option<&Composite> drop unifies both render_graph callers
onto one stub path; no dead arm, no second/root-only stub function, no scope
creep beyond graph.rs (production) + main.rs (test-only goldens/assertions).
No ledger prose names the root source:{kind} marker as canonical; the C9
cycle-0017 realization (where: definitions render) is not stale. No new invariant
introduced — the cycle realizes existing contracts more fully, so no realization
note is manufactured.
Regression: none configured in the profile (no-op) — the architect review is the
gate. Resolution: carry-on (drift-clean). NOT a milestone close.
Tracker reconciliation done this cycle (no code, orchestrator-applied):
- #36 closed — 0024 resolved it (params declared once on the recipe; the 8
lockstep tests deleted), but Gitea's `closes #43 #36` only auto-closed #43.
- #25 closed — 0024 removed the dyn Node::schema() re-call + bootstrap-derivation
duplication this issue named (signatures gathered once into FlatGraph).
- #24 retitled to derive_signature()/signature() — the panic-vs-typed-error
divergence persists under 0024's renamed symbols; still open, low-priority.
|
||
|
|
12b1bcec22 |
feat(aura-cli): render the root composite like any other composite
Close #49 and remove the last two render special-cases the blueprint root still carried, both pre-0024 vestiges. Since cycle 0024 the root is an ordinary Composite with input_roles, so the render no longer needs to treat it apart: (A) Fan-in slot stubs at the root. A composite-interior multi-input leaf already renders its wired slots as #… stubs; a top-level leaf did not, because render_blueprint threaded stub_ctx: None. It now passes the root composite (it IS a &Composite, carrying roles + edges), and 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). SimBroker now renders [SimBroker(#E,#price)] (#E = the Exposure producer's sibling-unique signature prefix, #price = the price source role name). (B) Role-named root entries. render_blueprint built entry markers from format!("source:{kind}") while render_definition uses role.name — the marker/stub asymmetry. The root now builds entries from role.name (filtered to bound roles, source.is_some()), identical to the interior, so the source marker reads [price] and matches the #price slot stub it feeds. render_compilat is deliberately untouched: post-inline the role name has dissolved (C23), so the compiled view keeps [source:F64] from the flat SourceSpec.kind — [price] pre-inline / [source:F64] post-inline is C23 made visible, and compiled_view_golden is the byte-unchanged negative control. Design call (orchestrator, with the user): the marker/stub asymmetry was fixed at its source (the entry naming) rather than by special-casing the stub to match the kind-named marker — the latter would have been a root-only stub behaviour, violating the "no second stub path" acceptance. The user chose full symmetry ([price], dropping the kind annotation) over a name+kind marker, prioritising structural cleanliness; the only remaining root-vs-interior distinction is the bound-role filter (C3), which is semantic, not cosmetic. Scope: crates/aura-cli/src/graph.rs only; read-only render (C9), behaviour-preserving for the run path (C1), no engine change. blueprint_view_golden re-captured from the live `aura graph` output (the where: section stays byte-identical — only the main graph re-flows); the SimBroker needle, a root-SimBroker-stub assertion on the macd view, and a [src] role-name-passthrough assertion are added. Verification (orchestrator-run, not agent-reported): cargo build --workspace green; cargo test --workspace 150 passed / 0 failed (same count, no run-path test moved); cargo clippy --workspace --all-targets -D warnings clean. compiled_view_golden and render_compilat byte-untouched. closes #49 |
||
|
|
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. |
||
|
|
ed1d22bbfa |
spec: 0025 render root like any composite (slot stubs + role-name entries)
Settled source: issue #49, enabled by cycle 0024 (
|
||
|
|
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 |
||
|
|
1b3909316e |
feat(aura): node signature lives in the blueprint; collapse Blueprint into Composite
Consolidate the node data structure so every node's signature (NodeSchema:
inputs/output/params) is declared once and exists in the blueprint pre-build,
and dissolve the special "root graph" type. Behaviour-preserving (C1).
Signature vs sizing
- NodeSchema is now the static signature only: InputSpec -> PortSpec{kind,firing},
with lookback removed. The signature is fully static per blueprint (input
kinds/firing, output fields, params); LinComb's variable arity is a builder arg,
not an injected param.
- The one param-dependent quantity, an input's buffer lookback (e.g. Sma's window =
its injected length), moves out of the signature to Node::lookbacks() -> Vec<usize>,
read only by bootstrap for sizing. Node::schema() is removed.
- LeafFactory -> PrimitiveBuilder, which carries the full NodeSchema. The built node
no longer re-declares it: closes the params-declared-twice drift (#36, the 8
per-node factory_params_match_built_node_schema lockstep tests are deleted — their
subject is now structurally impossible) and a value-empty recipe exposes its full
I/O interface pre-build (#43).
Root is just a bound composite
- struct Blueprint is deleted; its compile/bootstrap/param_space methods move onto
Composite. Role gains source: Option<ScalarKind> (None = open interior port,
Some = bound ingestion feed). A composite is runnable iff every root role is bound;
the "main graph" is no longer a category, only the fully-source-bound composite.
New error CompileError::UnboundRootRole for an open root role.
- BlueprintNode::signature() answers uniformly for both arms: Primitive returns the
builder's declared schema, Composite derives it from the interior (role kinds in,
OutField kinds out, aggregated params), pre-build, no build.
compile -> FlatGraph -> bootstrap
- compile validates structure pre-build via signature() (validate_wiring: range +
kind, returning the same variants as before, so an edge kind fault is now caught
before any build closure fires) and emits FlatGraph{nodes,signatures,sources,edges}.
- bootstrap consumes the FlatGraph: kinds/firing/output from the carried signatures,
buffer depth from node.lookbacks(). SourceSpec survives as the flat descriptor.
Renames: BlueprintNode::Leaf -> Primitive, LeafFactory -> PrimitiveBuilder.
Render (aura-cli/src/graph.rs) is migrated compile-only: it takes &Composite, maps
bound roles to the same source-entry shape, so both render goldens reproduce
byte-identical output (no re-capture needed). Render-fidelity tuning is the next cycle.
Verification (orchestrator-run, not agent-reported): cargo build --workspace green;
cargo test --workspace 150 passed / 0 failed; cargo clippy --workspace --all-targets
-D warnings clean. All pinned determinism/run-output tests pass with values unchanged;
no behavioural assertion was altered to go green. 5 new tests assert the signature is
pre-build and uniform, that compile rejects a kind mismatch without building (via a
panicking builder), UnboundRootRole, and lookbacks()/signature arity agreement.
Deferred to cycle-close audit (per plan): docs/design/INDEX.md and some aura-std
module docs still name the old Node::schema()/LeafFactory/BlueprintNode::Leaf/
Blueprint::param_space contracts; prose reconciliation is the architect's at audit.
closes #43 #36
|
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
481172ae2e |
feat(aura-cli): unify blueprint main-graph render through one shared core
render_blueprint and render_definition were two divergent paths: the main graph drew bare factory.label() leaves and bare edges, the composite interior drew enriched leaves (params, slot stubs, output bindings). They now delegate to one shared render_graph over borrowed slices — the blueprint is the root composite, differing only at the borders (ParamNames::Factory vs Aliases, Entry unifying SourceSpec and Role, empty OutField list = no bindings). LeafFactory holds a Box<dyn Fn>, so it is not Clone — an owned root-composite adapter is impossible; the shared core takes borrowed slices instead. The main graph now shows what the strategy does: a multi-output producer's selected field surfaces as a consumer-side prefix ([histogram -> Exposure(scale)]), mirroring the := output-binding form. Edge labels are deliberately avoided — ascii-dag 0.9.1 silently drops an edge label it cannot place without collision (arena_render.rs can_place_label), unreliable in a dense graph. Deferred: top-level blueprint multi-input leaves render without #-slot stubs (stub_ctx None) — fan_in_identifiers is still composite-coupled via signature_of. refs #48 |
||
|
|
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 |
||
|
|
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). |
||
|
|
3b496883e6 |
feat(aura): render composite output re-exports as producer bindings
In the blueprint view's `where:` section, a composite's output re-exports now fold onto their producing node's label as a binding (`[macd := Sub(#Ef,#Es)]`) instead of each becoming a standalone terminal node wired from its producer. An OutField re-exports an existing interior node's value — it is never a new node — so the old form drew false terminals (MACD's macd line feeds the signal EMA and histogram internally yet rendered as a dead-end leaf) and inflated the node count by the output arity (MACD where: 8 -> 6 nodes; sma_cross drops its [cross] stub + edge). The drawn graph is now exactly the computation DAG; the `:=` prefix is the visual signal that a node's value escapes the boundary. Input roles stay standalone entry nodes (no producer to annotate) — the asymmetry is deliberate. render_definition drops its per-OutField node+edge loop; a new output_binding helper yields the `name := ` / tuple `(a, b) := ` prefix. Pure render layer: C8/C23 untouched, OutField.name never reaches the compilat, compiled_view_golden byte-identical, MACD run deterministic and unchanged in metrics. Test blast radius was wider than spec 0022 §Components / the plan's Scope note counted: besides the MACD pinning test, four more sites assert a producer that carries an OutField and so gain the `:=` prefix — blueprint_view_defines_each_composite_once, the two fan-in identifier tests, the cli_run integration test, and (missed by the plan, caught at implement) the full-render blueprint_view_golden. All re-pins are forced by the design, zero judgement. The output_binding tuple arm is unexercised by current fixtures (no composite re-exports >1 field from one node). Verified: cargo test --workspace 0 failed; clippy --all-targets -D warnings clean. closes #46 |
||
|
|
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 |
||
|
|
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 |