Commit Graph

79 Commits

Author SHA1 Message Date
Brummel 2844ace2ce spec: 0054 per-instrument pip metadata channel
refs #22
2026-06-18 18:58:56 +02:00
Brummel 7deb38edd8 chore: cycle 0053 close — audit clean, retire spec + plan
Cycle 0053 (multi-tap trace join on timestamp, #93) closes drift-clean.

Audit (architect drift review over 066638e..HEAD against docs/design + the
CLAUDE.md domain invariants):
- C8/C18 honoured: join_on_ts is a post-run reduction over recorded
  (Timestamp, Vec<Scalar>) sink streams, a true sibling of summarize/f64_field
  in the report layer — correct architectural home.
- C3 not violated: the join runs post-run over already-recorded output, not
  in-graph; no as-of join smuggled into the DAG. C1 ts-uniqueness is a
  documented precondition, not a runtime check — consistent with how the
  engine leaves wiring-time invariants to contract.
- Option-per-side keeps the engine honest (reports presence; the consumer owns
  the 0.0/-1/false defaults). The hand-rolled *_by_ts HashMaps are fully gone;
  the two RED unit tests pin the #93 cardinality-misalignment + spine-drop.
- No drift, no debt, no ledger entry needs updating. Recommendation: carry on.

Regression gate: no dedicated regression scripts configured (optional per the
conventions); build/test/clippy --workspace --all-targets is the gate and ran
green — the gated GER40 byte-identity tests executed against the local archive
(not skipped), proving drain_trace's trace is byte-preserved across the
refactor.

Per the artifact lifecycle (committed while the cycle is live, git rm at cycle
close), the ephemeral spec and plan are retired here; their durable rationale
lives in the implementation commits 74324d1 / 35c5adc and the git history.
2026-06-18 15:04:42 +02:00
Brummel 24fa3fe155 spec: 0053 multi-tap ts-join
Ratified design for a reusable, spine-anchored join_on_ts helper in
aura-engine that fuses multi-tap Recorder traces on the recorded timestamp
(Option-per-side: the engine reports presence, the consumer interprets
absence). Refactors drain_trace onto it and documents the cadence rule on
the Recorder API. Resolves the #93 zip-by-index panic.

refs #93
2026-06-18 14:41:19 +02:00
Brummel 066638ec25 chore: cycle 0052 close — audit clean, retire spec + plan
Cycle 0052 (real-data source-open seam, Runway-1) closes drift-clean.

Audit (architect drift review over aedaa5d..HEAD against docs/design + the
CLAUDE.md domain invariants):
- C3 (one ms<->ns crossing) strengthened — the inverse is seam-private and the
  only consumer-side divide left; every consumer ns_to_ms / unix_ms_to_epoch_ns
  wrap is gone.
- C4 (O/H/L/C merge tie-break order) now in exactly one vetted home (open_ohlc);
  the hand-spelled four-field loops are gone.
- C12 (one shared Arc<DataServer>/cache) preserved; C7 surface additive.
- No drift, no debt, no ledger entry needs updating. Recommendation: carry on.

Regression gate: no dedicated regression scripts configured (optional per the
conventions); build/test/clippy --workspace is the gate and ran green — the gated
open_ohlc_seam behaviour-preservation test executed against the local archive
(not skipped), proving byte-identity vs the ms-path.

Per the artifact lifecycle (committed while the cycle is live, git rm at cycle
close), the ephemeral spec and plan are retired here; their durable rationale
lives in the implementation commit 8c9a1b4 and the git history.
2026-06-18 13:03:03 +02:00
Brummel fa9dac35aa spec: real-data source-open seam (Runway-1)
Consolidate aura-ingest's OHLC source-open surface onto the engine-native
epoch-ns Timestamp currency: a private seam-owned inverse, a Timestamp-window
opener, a canonical OHLC bundle opener (fixed C4 O/H/L/C order), and a
DataServer/DEFAULT_DATA_PATH re-export + default_data_server convenience — so a
real-data source builds from aura-ingest alone, with the one ms<->ns crossing
owned by the seam. Behaviour-preserving (byte-identical RunReports).

Grounded against the tree via the ground-spec-0052 workflow (25/28 assertions
confirmed; compare.rs compiler-invisible site + AC1 scope fix applied).

refs #80, #81, #92
2026-06-18 12:26:11 +02:00
Brummel 28958f2110 chore: retire docs/specs and docs/plans as committed artifacts
Per-cycle specs/plans are step-scoped: every file maps to already-shipped
code, their snippets drift hard against current APIs (InputSpec->PortSpec,
Sim->Harness, schema() removed, C16 zero-dep reversed, ascii-dag retired),
and a stale spec read as an API reference misleads agents into non-compiling
code. No durable knowledge lost — rationale lives in the ledger + code
rustdoc, history in git + the tracker.

- gitignore docs/specs/ and docs/plans/
- delete all 117 existing specs/plans (recoverable via git history)
- CLAUDE.md: specs/plans are local-only ephemeral artifacts
2026-06-18 11:06:37 +02:00
Brummel 0d7c2c16f4 docs: narrow streaming residency claim from whole-process RSS to the source ring
The C12 realization and the 0041 spec claimed streaming residency is O(one chunk) at the process level ("a 20-year window streams in the same memory as a one-day one"). That holds only for the aura source ring (M1FieldSource::resident_records(), bounded by one chunk and correctly tested); whole-process RSS grows O(records-touched) because data-server's FileCache retains each window's parsed chunks (~56 B/record) read-only for the pass. Reproduced: 6 MiB @ 1mo -> 173 MiB @ ~10y, linear.

That retention is the replay-many optimization (load-once; close+volume share one parse via the field-agnostic FileKey), not a leak, and no always-on eviction can bound a single forward pass without regressing it. This narrows the docs/comments to the true per-source-ring property and names the FileCache per-window retention as the real, replay-amortized process cost. The streaming_seam assert is unchanged (it was always the ring bound; only its labelling confused ring vs process). The deferred opt-in non-retaining streaming read path is parked as Brummel/data-server#2.

closes #95
2026-06-17 22:09:32 +02:00
Brummel 98be64ebdf spec(0051): strategy as a World-consumable Composite blueprint
Milestone spec: ship the GER40 session-breakout as a Composite blueprint so the
same strategy runs across backtest / sweep / walk_forward / compare without
re-authoring (the #94 fieldtest friction). Records the design decision that the
bar period is a structural axis (a construction arg binding both Resample and
Session, so a sweep cannot desync them — #96) and that the canonical shippable
strategy form is a Composite (FlatGraph is its compiled substrate). Authoring-
side; no engine-core change (the existing bind() + GraphBuilder + param_space()
layer already suffices).

refs #94, #96, #97
2026-06-17 19:06:11 +02:00
Brummel 90c1de841d fieldtest: GER40 session-breakout research deep-dive — 7 bins, 8 findings
Public-API research deep-dive driving the just-shipped GER40 real-data
session-breakout demo (5b5f034) through the escalating sequence a serious
researcher attempts next: scale to multi-year, walk-forward, structural-param
sweep, multi-instrument compare. Standalone consumer crate (path-deps only;
public interface = ledger + glossary + example corpus + cargo doc; no
crates/*/src read). All runs against the real /mnt/tickdata/Pepperstone archive.

Findings: 3 bugs, 1 friction, 3 spec_gap, 1 working. Triaged to the tracker:
- spec_gap: the flagship breakout ships as a raw FlatGraph the World API cannot
  consume (sweep / walk_forward / compare all forced a hand re-author to a
  Composite, which reproduced the FlatGraph numbers EXACTLY) — refs #94.
- bug: process residency is O(window) (6->173 MiB across 1mo->10y) despite the
  documented O(one-chunk) claim; the aura ring stays <=1024 but data-server
  retention scales, and streaming_seam.rs only measures the ring — refs #95.
- bug: the bar-period is one knob split across Resample (tunable param) and
  Session (baked) and desyncs silently to 0.0 pips; Delay.lag leaks into the
  default param_space (the C34 deform-vs-tune discriminator) — refs #96.
- spec_gap: the walk_forward empty-space idiom for param-free strategies is
  undefined — refs #97.
- spec_gap (no per-instrument pip registry) and friction (one-directional
  ns->ms conversion) re-confirm the known #22 and #80.
- working: C1 determinism over 7y / 148k bars, the lazy streaming source, and
  the sweep/WFO cores held under real multi-year load; the Composite re-author
  was behaviour-preserving (C23).

Scratch consumer crate + run transcripts committed under fieldtests/ per the
fieldtest convention (matching cycle-0049). The crate path-deps the engine and
reads the real archive; its bins are the repros cited in the issues above.
2026-06-17 18:55:04 +02:00
Brummel 16adb2b949 docs(specs): 0050 — session-breakout node vocabulary (milestone spec)
Milestone 'Strategy node vocabulary I — temporal, logic, resample & session':
the 7 aura-std nodes (EqConst, Gt, And, Delay, Latch, Resample, Session) plus a
synthetic e2e fixture, driven by the GER40 15m session-breakout. Records the
design pass's cross-cutting decisions: close-instant timestamp convention,
Latch emits f64 (Exposure dropped from the DAG), the EqConst i64->bool gate,
the Resample-only Barrier(0) firing regime, and the build order. refs #84-#91.
2026-06-17 16:41:31 +02:00
Brummel 562791e613 fieldtest: milestone the-world-orchestration-families — 4 examples, 4 findings
Milestone-close gate for "The World, part II — orchestration families".
Four end-to-end scenarios derived top-down from the milestone promise,
each built from HEAD and run as a downstream consumer through the public
API + CLI only (no crates/*/src read):

- mw_3 (headline): real EURUSD-M1 walk-forward composing all four axes
  (grid sweep + optimize in-sample, OOS stitch) over the M1FieldSource
  seam + lineage — 5 windows, 145 200-pt stitched curve, C2 held.
- mw_2: monte_carlo over 128 seeds, reproducible McFamily (C1) + aggregate.
- mw_1: random sweep -> optimize -> 64-member lineage round-trip.
- mw_4: family-lineage CLI transcript.

Findings: 4 working (promise empirically delivered), 1 bug, 3 friction.
- bug F5: `aura runs list`/`rank` cannot read a pre-0047 runs.jsonl — the
  cycle-0047 typed-Scalar params migration (86746e3) left the persisted
  flat store unmigrated; load() fails at the first bare float (+ exits 0).
- friction F6: no epoch_ns_to_unix_ms inverse for real-data walk-forward.
- friction F7: DataServer/DEFAULT_DATA_PATH not re-exported by aura-ingest.
- friction F8: family store is directory-keyed, undocumented on Registry::open.

Spec: docs/specs/fieldtest-milestone-the-world-orchestration-families.md
2026-06-17 15:46:18 +02:00
Brummel af0191884d fieldtest: cycle-0049 — 4 examples, 7 findings
Public-API field test of the random param-sweep surface, from a standalone
downstream-consumer crate (path-deps only; the public interface = ledger +
glossary + spec 0049 + cargo doc rustdoc; no crates/*/src read). Four bins,
each built from HEAD and run: continuous tuning (200-point random tune ranked
by total_pips), the typed validation gate (all five reachable SweepError
variants pre-run), reproducibility + seed-sensitivity + the full i64::MIN..=MAX
sampler edge, and Space-trait interchangeability (one tune_and_rank<S: Space>
over both GridSpace and RandomSpace).

Findings: 0 bugs, 4 working, 2 spec_gap, 1 friction. The four working findings
confirm the cycle's acceptance criterion empirically — the headline tune reads
as the code a researcher would write, the gate is precise and fires before any
run, the C1 reproducibility promise is checkable in one line, and the Space
trait delivers one-consumer/both-enumerations.

Triage of the actionable findings:
- friction (no named-axis builder for RandomSpace — positional Vec<ParamRange>
  must align with param_space() by hand, and a same-kind transposition passes
  validation silently): filed as a feature for a future cycle, refs #79
  (a RandomBinder sibling to the grid's SweepBinder).
- spec_gap (SweepError rustdoc summary named only GridSpace): fixed inline in a
  follow-up doc commit.
- spec_gap (NonNumericRange / Bool-slot ranges unreachable with the shipped
  aura-std node roster — no node declares a Bool/Timestamp knob): RATIFIED as
  intentional. The variant is a forward-looking structural guard for the C16
  "author your own node" path (a Bool/Timestamp param-slot a custom node may
  declare); its current untriggerability with the standard roster is expected,
  not drift.

refs #79
2026-06-17 13:42:39 +02:00
Brummel 5b3d133529 spec: 0049 random param-sweep (boss-signed)
RandomSpace + a Space trait + typed ParamRange: the random half of the
C12.1 param-sweep axis (grid shipped in 0028). N seeded uniform draws over
declared continuous ranges, fed to the same enumeration-agnostic sweep core.
Param-only sweep signature and a code-path-disjoint SplitMix64 instance keep
the World-II source-seam firewall (refs #71) intact.

Auto-signed under /boss spec auto-sign: fresh grounding-check PASS and a
unanimous five-lens spec-skeptic panel (criterion, grounding, scope-fork,
ambiguity, plan-readiness all SOUND).

refs #52
2026-06-17 12:36:39 +02:00
Brummel 791b809413 spec: 0048 walk-forward param plane
Settled-design spec for the walk-forward param-plane cut: WalkForwardResult gains space: Vec<ParamSpec>, WindowRun.chosen_params Vec<Scalar> -> Vec<Cell>, walk_forward takes the space by value (Fn-closure generic untouched), and param_stability schema-checks a per-slot coercer (Bool -> 0/1, Timestamp unreachable per C20) before a type-blind reduction, deleting scalar_as_f64 + its per-value unreachable.

Behaviour-preserving (C1): param_stability yields identical MetricStats over the existing fixtures. Overturns 0047's Fork A (chosen_params stays Vec<Scalar>) — the WF structs carry no serde derives, so the serializability defense is moot; the substantive case is C7 purity + SweepFamily symmetry. grounding-check PASS.

refs #75
2026-06-16 18:42:20 +02:00
Brummel 73e1558383 spec: 0047 cell hot-path carrier (boss-signed)
Auto-signed under /boss spec auto-sign: all objective gates green
(Step-1.5 precondition, Step-4 self-review, Step-5 grounding-check PASS)
and a unanimous five-lens spec-skeptic panel SOUND, after one editorial
round on the ambiguity lens (the test-migration rule was sharpened to a
three-role classification: eval-output -> Cell, column-write -> Scalar,
channel-send -> Scalar).

Spec for the deferred C7/C8 carrier swap flagged by the C7 realization
note: Cell becomes the hot-path value carrier (Node::eval ->
Option<&[Cell]>, node out-buffers, inter-node forward via a new
AnyColumn::push_cell), and Scalar narrows to the self-describing dynamic
boundaries (the param path, AnyColumn::get, source ingestion).
Behaviour-preserving (C1): only the carrier type narrows.

refs #74
2026-06-16 12:46:18 +02:00
Brummel 119221c8bb spec: 0046 sweep named-binding (C12 #57)
Thread a derived named view of a sweep point so consumers stop re-zipping
param_space() names onto the bare &[Scalar]. A free function zip_params over
(ParamSpec names ⊗ positional point) in aura-core; GridSpace retains the
ParamSpec list it already receives in new(); SweepFamily carries it and
exposes named_params(i). The run-closure signature
`F: Fn(&[Scalar]) -> RunReport + Sync` stays byte-for-byte (honours #52's
#71-firewall constraint, so RandomSpace plugs in with zero reconciliation).
The lossy f64 collapse moves into the manifest constructor; the view stays
typed. Behaviour-preserving; SweepPoint and enumeration untouched.

Design ratified in-context (brainstorm, approach C over A, free function
over a NamedPoint type); a reconciliation comment on #57 records the
resolved forks with provenance. Human sign-off after the auto-sign panel's
grounding lens caught a blast-radius undercount in the first draft —
corrected to all eight sim_optimal_manifest call sites plus the
aura-registry SweepFamily struct-literal.

refs #57
2026-06-15 23:07:09 +02:00
Brummel bf829f605b spec: 0045 registry lineage for orchestration families (boss-signed)
Persist sweep / Monte-Carlo / walk-forward runs as named, linked families
(C18 lineage / C21): a FamilyRunRecord stamps each member with a shared
family_id = "{name}-{counter}" in a sibling families.jsonl store, group_families
re-derives a family as a unit, and the CLI gains aura mc + family-aware aura runs.
Producer-supplied window via Source::bounds()/window_of (the #71 firewall: no Vec
scan, no input artifact). family_id form (name + counter, not a content-hash)
resolved by the user in-session; reconciliation comment on the issue records it.

Auto-signed under the boss spec-auto-sign gate: all objective gates green
(precondition, self-review, grounding-check PASS) and a unanimous five-lens
spec-skeptic panel (criterion, grounding, scope-fork, ambiguity, plan-readiness).

refs #70
2026-06-15 17:29:27 +02:00
Brummel a4712eb336 spec: 0044 walk-forward family (C12 axis 3)
Walk-forward orchestration: a WindowRoller rolls (in-sample, out-of-sample)
splits over a time span, walk_forward runs a disjoint harness per split via the
shared run_indexed core (C1), and stitches the OOS pip-equity segments into one
continuous curve. The varying dimension is the data window (axis 3), realized
eager-agnostically (#71): the public surface carries WindowBounds + a per-window
closure, never a materialized stream Vec; C2 no-look-ahead is a pure bounds
invariant (oos.0 > is.1), checkable with zero ticks.

The in-sample optimize (axis 2) is closure-supplied, not called by the engine:
aura-engine cannot depend on aura-registry (C9), and C12 forbids baking search
policy into the primitive. The CLI bridges both crates in run_walkforward.

Param-stability shape (the load-bearing fork) resolved with the user as R2:
on-demand, not stored. WalkForwardResult stores only raw per-window outcomes +
the stitched curve; param_stability(&result) -> Vec<MetricStats> is a public
on-demand reduction over the retained per-window chosen params (sweep-precedent:
summaries computed on demand, not cached, vs McFamily's stored aggregate). A
stored summary would be recomputable from the raw windows (redundant) and would
force one statistic canonical when "stability" admits several. Recorded with
provenance in the #69 reconciliation comment. MetricStats::from_values is
extracted from McAggregate::from_draws (behaviour-preserving; the 7 mc tests
guard) so the MC aggregate and the helper share one reduction. Empty OOS segment
contributes 0.0 to the stitch offset (mirrors summarize's unwrap_or(0.0)).

Gates: Step-1.5 precondition clean (no fork silently picked), self-review clean,
grounding-check PASS (twice — re-run after each edit). Auto-sign panel escalated
on the scope-fork design lens (param-stability shape) to human sign-off; the user
resolved it (R2), so this is a user-signed spec, not boss-signed.

refs #69
2026-06-15 14:56:12 +02:00
Brummel 0bbc8190b8 spec: 0043 monte-carlo family — McFamily over a seed set (boss-signed)
C12 axis 4: Monte-Carlo as a sweep over seeds. `monte_carlo(base_point, seeds,
closure) -> McFamily`, the analog of SweepFamily, reusing the disjoint-parallel
executor (extracted into a shared `run_indexed` core) rather than forking a new
run loop. Each realization is a disjoint C1 unit; parallelism is across draws.
The aggregate (V1) covers all three run metrics with mean + p5/p25/p50/p75/p95,
a pure post-run reduction over the retained raw draws.

Eager-agnostic firewall (#71): the API takes seeds + a per-draw closure, never a
materialized stream Vec; seed->Source construction stays a closure-body concern.

Signed under the /boss spec auto-sign gate: 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.
The aggregate fork was settled to V1 by the user (issue #68 reconciliation
comment, verbatim "ja, wir machen v1", 2026-06-15).

refs #68
2026-06-15 13:35:48 +02:00
Brummel 5604c420c5 spec: 0042 seed-as-input — seeded source + live RunManifest.seed (boss-signed)
A seeded source whose stream is fully determined by a u64 seed, wired so the
seed reaches RunManifest.seed at the manifest-construction site (Fork B: seed at
the data-generation edge, outside the engine graph). Contract is a producer
Fn(u64) -> impl Source, never seed -> Vec, so the Monte-Carlo family can re-seed
N times without materializing N streams. Deterministic dependency-free PRNG
(SplitMix64) for C1 bit-stability. Precondition for #68 (Monte-Carlo) and #52
(random sweep).

Auto-signed under /boss spec auto-sign: objective gates green (precondition,
self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel
(criterion, grounding, scope-fork, ambiguity, plan-readiness all SOUND) after one
editorial-repair round.

refs #66
2026-06-15 12:03:43 +02:00
Brummel ae240140c4 spec: 0041 source ingestion seam (boss-signed)
Re-types the engine's ingestion input from a materialized
Vec<Vec<(Timestamp, Scalar)>> into a `Source` producer trait the k-way
merge drives by peek/next, and proves it against a real, lazily-streamed
data-server window in the same cycle — closing the cycle-0011 deliberate
eager-materialization gap (C12 Arc<[T]> cross-sim sharing). A VecSource
adapter keeps every existing run call site byte-identical
(behaviour-preserving); an M1FieldSource streams data-server chunks with
O(one-chunk) residency, independent of window length.

Foundational cycle of the World-II milestone; #66/#68/#69 inherit the
seam. Signed under the boss auto-sign gate: precondition clean, grounding
PASS on the final bytes, unanimous five-lens spec-skeptic panel (two
editorial ambiguity rounds on the residency predicate repaired to a
measured per-pull CHUNK_SIZE ceiling).

refs #71
2026-06-15 10:00:44 +02:00
Brummel 21c1621bd0 docs,engine: drop coined "compilat", use FlatGraph / "flat graph"
"Compilat" (German "Kompilat") was a coined noun for the product of the
bootstrap compilation — neither English nor a natural fit. The runtime
artifact already has a code identifier for exactly this thing: the
`FlatGraph` struct (harness.rs). Replace the coinage with that identifier:

- prose mentions          -> "flat graph" (mirrors the type, reads plainly)
- definitional anchors    -> `FlatGraph` (C11, C23, the running-graph line)
- `render_compilat`       -> `render_flat_graph` (historical render symbol;
                             keeps the `render_blueprint` / `render_flat_graph`
                             source-vs-product pairing)
- `intra-compilat`        -> `intra-graph`
- `from_compilat` (test)  -> `from_flat`

"compilation" / "re-compilation" / "bootstrap-as-compilation" (the process,
ordinary English) are deliberately left untouched. Behaviour-preserving:
only comments, design ledger, specs/plans, one test-local variable and its
assert messages change. Full workspace test suite green; clippy clean.
2026-06-14 17:02:15 +02:00
Brummel 69b16f7d39 spec: 0040 correct blast radius to four (empirically probed)
The boss-signed 0040 spec's blast-radius section claimed "exactly three" flipping
tests, named from a hand-trace. A throwaway probe (the check implemented, full
workspace suite run, then discarded) found FOUR: the three named negative tests plus
`param_space_mirrors_compiled_flat_node_param_order_under_nesting`, a compile-success
param-order test that today compiles a deliberately under-wired nested graph (LinComb's
two term inputs never wired) just to read its flat param order. The new check rejects
it where it currently returns Ok.

Correction is editorial, not a design change: the check itself is unchanged. The
fourth test's fix differs from the three negative tests — it needs the contrived graph
fully wired (fan fast_slow's output into both LinComb terms + a covering root role),
not just a covering role — so the §Blast radius and §Acceptance criteria now enumerate
all four with their distinct fixes. Verified empirically (119 passed, 4 failed; every
other crate green), the strongest possible grounding for the count.

refs #65
2026-06-14 16:14:52 +02:00
Brummel 07b1ae1c63 spec: 0040 wiring-totality-check (boss-signed)
Reject graphs with an unwired or double-wired interior input port. A new
name-free structural check `check_ports_connected` in `validate_wiring`
(crates/aura-engine/src/blueprint.rs) requires every interior node's every
declared input slot to be covered by exactly one wiring act — one `Edge{to,slot}`
or one `Role` `Target{node,slot}`, counted uniformly. Zero coverage is the new
`CompileError::UnconnectedPort` (a forgotten connection that today bootstraps a
silent empty column, harness.rs:137-148); more than one is `DoubleWiredPort` (a
slot holds one column). Run once at the existing compile boundary and recursive
per nesting level, so both the raw `Composite::new` and the `GraphBuilder::build`
surfaces inherit it. Index-based, name-free — the compilat is untouched (C23).

Narrowed scope of #65: the original "promote names to load-bearing wiring keys /
close the exposure-price swap structurally" goal is dropped (see the reconciliation
comment on the issue) — #64 already moved authoring to handles + visible port
names, so the residual same-kind swap is a valid-but-wrong name choice no
structural check catches, and a structural fix would push domain semantics into
the domain-free engine (C10/C7). This cycle ships only the name-free half that
stands on its own.

Auto-signed under /boss: precondition gate clean (no fork silently picked),
self-review + parse-trace clean (no spec-validation parser declared -> documented
no-op), grounding-check PASS on the final bytes (one BLOCK on an incomplete
blast-radius survey, repaired forward by naming the three flipping negative tests),
and a unanimous five-lens spec-skeptic panel (criterion, grounding, scope-fork,
ambiguity, plan-readiness all SOUND).

refs #65
2026-06-14 16:02:09 +02:00
Brummel 292c95756f spec: 0039 graphbuilder-name-based-wiring (boss-signed)
Add a typed-handle GraphBuilder authoring surface for blueprint topology:
node references become Copy NodeHandle values, ports/fields resolve by name
against the existing PortSpec.name/FieldSpec.name at a single fallible build()
terminal, lowering to the unchanged index-wired Composite (C23 holds by
construction — names never reach the compilat, mirroring param-name resolution).

Spec auto-signed under /boss: all objective gates green (precondition,
self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel
(criterion, grounding, scope-fork, ambiguity, plan-readiness all SOUND).

The structural close of the SimBroker exposure/price swap (#21) is explicitly
out of scope, tracked as #65.

refs #64
2026-06-14 02:07:09 +02:00
Brummel 47179ce5a4 spec: 0038 dedup-sma-cross-test-fixtures (boss-signed)
Collapse the three verbatim-duplicated SMA-cross harness test fixtures
(synthetic_prices, sma_cross, composite_sma_cross_harness) in aura-engine's
blueprint.rs and sweep.rs #[cfg(test)] modules into one shared crate-root
#[cfg(test)] mod test_fixtures. Behaviour-preserving, test-only.

Auto-signed under /boss: all objective gates green (precondition, self-review,
grounding-check PASS) and a unanimous five-lens spec-skeptic panel returned
SOUND.

refs #53
2026-06-14 00:04:55 +02:00
Brummel 42850c6f11 spec: 0037 bound-param-in-graph-model (boss-signed)
Surface a bind-bound param in the graph model and the `aura graph` viewer
signature instead of dropping it. A node built with `.bind(slot, value)` keeps
the slot off the tunable surface (param_space / sweep axes — unchanged, correct)
but now ANNOTATES it in the render: all slots shown, the bound one as `name=value`,
dimmed. The cycle-0036 `blend` renders `LinComb[weights[0], weights[1], weights[2]=0.5]`.

Structurally a twin of cycle 0035's instance-name thread: a conditional field
(here `"bound"`) threaded engine -> model -> viewer, plus goldens and a headless
render guard. Render/debug surface only — dropped at lowering (C23), model stays
deterministic (C14).

Auto-signed under the /boss spec auto-sign gate: precondition clean (the one open
notation decision was settled with the user in-session and recorded as a
provenance-bearing reconciliation comment on the issue), self-review + parse-trace
clean, grounding-check PASS, and a unanimous five-lens spec-skeptic panel.

refs #63
2026-06-13 23:05:28 +02:00
Brummel e7393f6809 spec: 0036 sample-showcase-blueprint (boss-signed)
Enrich the `aura graph` / `aura sweep` sample blueprint into a "trend + momentum,
weighted blend" strategy that showcases four authoring/viewer capabilities the
single-level sample left dark: multiply-nested composites, a multi-param node
inside a composite, a multi-output (MACD-like) node, and bind() (a param fixed as
a structural constant, dropped from the sweep surface). Pure authoring over
already-shipped primitives — no engine semantics or viewer change; reuses the
existing sma_cross/macd builders.

Auto-signed under /boss (spec auto-sign enabled): objective gates green
(precondition clean, self-review clean, grounding-check PASS) and a unanimous
five-lens spec-skeptic panel (criterion, grounding, scope-fork, ambiguity,
plan-readiness) returned SOUND. Design provenance recorded on #62.

refs #62
2026-06-13 19:58:27 +02:00
Brummel a421b5a8e1 spec: 0035 node-name-in-graph-model (boss-signed)
Surface a node's explicit instance name in the `aura graph` viewer: a named leaf
renders its box head as `fast: SMA[length]` (the instance name as a `:`
declaration prefix), an unnamed leaf keeps the bare `SMA[length]`. The name
crosses two surfaces — a conditional `"name"` field in the JSON graph model
(engine half) and a `cellLabel` prefix in graph-viewer.js (viewer half).
Explicit-`.named()`-only; `node_name()`'s lowercased-type default is not
surfaced. `named()`'s non-empty rule is kept with its code unchanged, its doc
re-grounded to the load-bearing invariant (knob-address segment + Some/None
prefix switch).

Signed via the /boss auto-sign gate: all objective gates green (precondition,
self-review, grounding-check PASS) and a unanimous five-lens spec-skeptic panel
(criterion, grounding, scope-fork, ambiguity, plan-readiness). Two earlier blocks
were resolved before the unanimous round — a separator-ambiguity by an editorial
fix, and a scope-fork (which nodes carry the prefix) by an explicit user decision
recorded as a provenance-bearing reconciliation comment on the issue.

refs #58
2026-06-13 18:38:47 +02:00
Brummel 9e57caebb8 spec: 0034 blueprint-constant-bind (boss-signed)
Add PrimitiveBuilder::bind(slot, value): fix a node param as a structural
constant, removed from param_space entirely (not pinned in the injected
vector). Closes the gap #55 names — param-bearing nodes (Sma/Exposure/LinComb)
had no constant path, only the no-sweep-mode SimBroker precedent.

Settled direction (Option B, builder-level): .bind() shrinks the builder's
declared param surface and wraps its build closure to re-splice the constant;
the construction layer (collect_params/lower_items/param_space) is unchanged
because both already key off builder.params(). Addressing is by-name
(authoring address space, C23/0032); compilat stays index-wired.

Auto-signed under /boss spec_auto_sign: objective gates green, grounding-check
PASS, unanimous five-lens spec-skeptic panel SOUND (one editorial repair round:
corrected the harness param_space assertion and pinned the exactly-one-match
bind contract).

refs #55
2026-06-12 16:12:09 +02:00
Brummel c8634d7fa9 spec: 0033 unify-runreport-json-serde (boss-signed)
Settled-source direct entry from issue #54 (no brainstorm). Retire the
hand-rolled RunReport::to_json writer and render stdout through serde, so a
record's stdout JSON is byte-identical to its runs.jsonl line, and pin that
identity with a test. Removes the last hand-rolled RunReport JSON writer
(amended C16). Disk shape unchanged; graph-model JSON (model_to_json) out of
scope (#58/#28).

Auto-signed under /boss spec_auto_sign: objective gates green (precondition
clean, self-review clean, grounding-check PASS) + unanimous 5-lens spec-skeptic
panel (criterion/grounding/scope-fork/ambiguity/plan-readiness all SOUND). The
grounding juror re-ran serde_json 1.0.150 against the canonical-form fixture and
confirmed the spec's "After" bytes. The one C16-scrutinized consequence
(serde_json dev-dep -> normal dep in aura-engine) is named and justified in the
spec; scope-fork juror confirmed it is settled by amended C16, not a silent pick.

refs #54
2026-06-11 19:33:33 +02:00
Brummel 7d04cb313a spec: 0032 correct test 5 fixture nesting (boss-signed)
Test 5's asymmetric-collision fixture asserted a root-level param_space
["sma.length"] while also being "test 2's fixture with one leg swapped" (nested)
— a contradiction: check_fan_in_distinguishability only descends into composites,
so a root-level collision is not rejected today (not RED-first). Caught at plan
time while writing the verbatim fixture body.

Corrected: test 5 nests the asymmetric collision in an inner composite `asym`
(param-bearing Sma "sma" + paramless Pass1 .named("sma") + Sub, both leaves on
role price fanning into the Sub) under a source-bound root, asserting
param_space() == ["asym.sma.length"] (the single injective entry — the paramless
leg contributes no path) and that compile_with_params(&[I64(2)]) is Ok after.

Re-grounded PASS, re-panel unanimous SOUND. Forward correction on top of 7f059d0
(main is forward-only — no rewind of the boss-signed spec).

refs #59
2026-06-11 18:15:37 +02:00
Brummel 7f059d0d94 spec: 0032 param-namespace-injectivity (boss-signed)
param_space() injectivity becomes a stated, enforced compile invariant. One
structural check (check_param_namespace_injective) replaces the fan-in machinery
(check_fan_in_distinguishability, check_composite_fan_in, signature_of,
leaf_has_param) and the AmbiguousKnob binder branch. The error is path-carrying
(DuplicateParamPath) and the binders run the check before name resolution, so the
canonical by-name author sees the structural cure (.named(...)) instead of the
AmbiguousKnob symptom. param_space() stays infallible; the invariant lives in the
compile step (C23).

Two render-identity-only fan-in rejections (asymmetric param/paramless collision,
role-vs-leg signature collision) are intentionally dropped — neither is a
param_space duplicate (every knob keeps a unique path), and the property they
guarded (render distinguishability) has had no live consumer since the renderer
was retired in 0026. The larger is pinned by a now-compiles fixture.

Boss-signed under spec_auto_sign: objective gates green (precondition clean,
self-review clean, grounding-check PASS without override) + a unanimous five-lens
spec-skeptic panel. The grounding lens first BLOCKed a false set-equality claim;
the user ratified dropping both render-identity cases, the spec was reframed onto
the injectivity scope, re-grounded PASS, and the re-panel returned unanimous SOUND.

refs #59
2026-06-11 18:06:28 +02:00
Brummel 11dfff860c fieldtest: cycle 0031 node-naming — naming cure under-signposted on the by-name flow
Per-cycle fieldtest of node-instance naming (#56), driven as a downstream
consumer from the public interface only (ledger + spec + rustdoc + CLI; no
crates/*/src read), three fixtures built and run from HEAD.

Verdict: the core 0031 promise holds first-try. A consumer authors
Sma::builder().named("fast"), inspects param_space() (sma_cross.fast.length),
binds by name and runs; the default-name case (sma.length / exposure.scale,
verbatim lowercase) and paramless-interchangeable-stays-legal both hold.

0 bugs, 3 working, 2 friction, 1 spec_gap. The one real gap (verified by the
orchestrator against the fixture output): the spec's headline forcing function
IndistinguishableFanIn does NOT reach an author on the canonical
.with(...).bootstrap() flow. An un-named 2-SMA cross emits a literal DUPLICATE
knob (sma_cross.sma.length x2); the binder resolves names before the compile
fan-in check, so the author hits UnknownKnob / AmbiguousKnob("sma_cross.sma.length")
— which point at the knob, not at the cure "name your nodes". IndistinguishableFanIn
only surfaces via the positional compile_with_params path. Rejection still
happens (no invalid blueprint runs), so it is an ergonomic/signposting gap, not
a correctness bug. Routed to the backlog (relates to #58); 0031 stays
audit-closed. Minor: FlatGraph/Harness lack Debug, so a bootstrap Result can't
be {:?}-printed.
2026-06-11 12:18:29 +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 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 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 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 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 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 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 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 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 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 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