Run a harness from a serialized blueprint (aura run <blueprint.json>)
#165
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The keystone of the World/C21 milestone: turn a serialized C24 blueprint into an actual run from the CLI, closing the data → bootstrap → run loop the cycle-0090 milestone fieldtest flagged as friction (the CLI could author and emit topology but not run an emitted blueprint).
Deliverable
aura run <blueprint.json> [--params <json>]:blueprint_from_json), compile via the injectedstd_vocabularyresolver, bootstrap (params + data + seed), run the harness, persist a RunReport;topology_hashfield from the start.Acceptance (validated property)
aura run, produces bit-identical metrics / traces to the Rust-builder path for the same topology + params + seed + data (C1).topology_hash= SHA256 of the canonical (#164, no-trailing-newline) blueprint form.topology_hashis a Tier-1 optional manifest field (serde default / skip), so existing manifests deserialize unchanged (#156).aura run <demo>.json.Settled forks (charter, ratified by the user "macht alles sinn, leg los" — 2026-06-30)
aura run <blueprint.json>(data-driven is the default going forward; a--blueprintalias can follow if wanted).--params '[{"F64":0.5},{"I64":2}]'(matchescompile_with_params, used by the sweep path); by-name--bind name=valuedeferred as a fast follow-on.blueprint_to_jsonform, stored as a hex string inmanifest.topology_hash.Notes
UnknownNodeType).Part of the World/C21 milestone (run + orchestrate families from blueprint-data).
Derived design decisions (cycle-1 grounding, specify Step 1.5)
A read-only recon of the existing run plumbing settled the one load-bearing
fork the charter left implicit. Recorded here before spec production.
Fork — what does
aura run <blueprint.json>actually run? A serialized C24blueprint is a SIGNAL (a strategy
Composite, ≤1 output): recording sinks(
Recorder, runtimempsc::Senderidentity), brokers (SimBroker,construction-arg builder), the data source, and the clock are deliberately OUT
of the round-trippable set (C24 ledger, "Out of the first cut's round-trippable
set"). So
aura runcannot "just run" a blueprint — it must WRAP the loadedsignal in a runnable harness.
Decision (derived):
aura run <blueprint.json>wraps the loaded signal inthe existing Stage-1-R run scaffolding — the same broker + equity/exposure
recording sinks + data binding that
run_stage1_r(crates/aura-cli/src/main.rs)builds around its hard-wired signal
Composite— then compiles(
compile_with_params), bootstraps (Harness::bootstrap), runs, and emits aRunReport. Basis: (1) the existing single-run path already establishes exactlythis wrap → compile → bootstrap → run → manifest shape; (2) wrapping the loaded
twin and the Rust-built twin IDENTICALLY is what makes the acceptance "bit-
identical to the Rust-builder path" (C1) a real, testable property — it is
precisely the
blueprint_serde_e2e.rsRecorder-wrapping pattern lifted to theCLI; (3) it keeps node logic Rust and only topology as data (C17), the broker /
sinks supplied at run, not serialized (honoring the C24 deferral).
Sub-decisions (derived, all precedent-established in the existing run path):
--pip-size/ instrument sidecar /default), as
run_stage1_rdoes — not added to the serialized blueprint.--real <SYM> [--from --to]opens the real M1 source.
--seed(default 0); window probed from the sourcespost-run (
window_of), as today.#164, no-trailing-newline)blueprint_to_jsonof the LOADED SIGNAL blueprint, as a Tier-1 optionalRunManifestfield (serde default / skip — old manifests load unchanged, #156).The user-ratified surface forks (positional
aura run <blueprint.json>;positional
--paramsJSON cells; SHA256 hash) are in the issue body("Settled forks", ratified "macht alles sinn, leg los" — 2026-06-30).
This is a derivable wiring task over proven substrate, not a new design — ready
for spec production.
Grounding refinements from plan-recon (derived decisions)
A read-only plan-recon found the wrapping seam is not a clean line-lift and
left three derivation questions. All four are derivable from the code + the C24
serialization requirement; resolved here.
Q1 — the seam is a behaviour-preserving RESTRUCTURE, not an extraction.
stage1_r_graph(crates/aura-cli/src/main.rs:2631-2790) co-builds the SIGNAL(fast/slow
Sma→Sub→Bias) and the WRAPPING (broker /Recorder/SeriesReducer/RiskExecutor/ cost-graph) in ONEGraphBuilder, with asingle
g.feed(price, [...])into both and the wrapping reading the signal'sinternal handle
exposure.output("bias"). There is no standalone signalCompositetoday.Decision: restructure it into
stage1_signal() -> Composite(thefast/slow/sub/bias signal, exposing a
priceinput-role + abiasoutput at its boundary — the serializable shape) and
wrap_stage1r(signal, pip) -> Composite(the broker / sinks / exec / costwrapping), with
stage1_r_graph() = wrap_stage1r(stage1_signal(), pip).Basis: this is intrinsic, not optional — a serialized signal MUST have a
clean boundary (only a bounded signal round-trips), and reusing the Stage-1-R
wrapping requires separating it from the signal; both force the same split. The
blueprint_serde_e2e.rstemplate (a nested signal-Composite with a price role +single bias output, wrapped with a
Recorder) proves the boundary shape works.Behaviour-preserving:
run_stage1_r's metrics + traces stay identical (gated byits existing tests + the bit-identical e2e).
Q2 —
manifest.paramscomes from the signal'sparam_space(), not ahard-coded tuple.
run_stage1_rhard-codes a 5-named-param tuple. Thedata-driven path derives the names from the loaded signal's
param_space()(its open-param names) zipped with the
--paramsvalues.Q3 — param threading.
wrap_stage1rbinds its OWN params (pip etc.),leaving only the signal's params open on the wrapped graph; the shared seam
calls
wrapped.compile_with_params(signal_params). So--paramsis exactly thesignal's open-param vector (
param_space()ordered), andrun_stage1_rpassesits values through the same seam (no more
compile_with_params(&[])on a fullypre-bound graph).
Q4 — "behaviour-preserving" semantics (the AC tension). When
run_stage1_rroutes the shared seam, its manifest GAINS
topology_hash(a new key) — this isby design, not a regression: every run becoming self-identifying is exactly
what #158 wants. "Behaviour-preserving" here means metrics + traces identical;
the manifest additionally carrying
topology_hashis the intended feature. Thestructural stage1-r test (
run_stage1_r_synthetic_folds_an_r_block) stays green;no exact-JSON pin exists on the stage1-r manifest to break (recon-verified).
Blast radius note (for the plan): adding
RunManifest.topology_hashwithout aDefaultderive is a hard compile break at 24 literalRunManifest { … }sites across 7 crates (recon-enumerated), not the two the spec names — each gets
topology_hash: None. The hash helper +sha2live inaura-cli(the seam),never in the frozen
aura-engine(invariant 8; #158 is research-side).Task-4 block resolved:
--pip-sizedroppedThe cycle-1 implement run (Tasks 1-3 shipped in
ff4a1b3) blocked Task 4 on acontradiction the plan introduced: it asked for a
--pip-size <n>flag, but theshared
run_signal_stage1r(signal, params, data, seed)seam takes nopip_size(pip is derived inside the run path) and no consumer for the flag exists.
Decision (derived): drop
--pip-size.pip_sizeis data-derived —SYNTHETIC_PIP_SIZEfor a synthetic run, the instrument sidecar for--real,exactly as
run_stage1_ralready does. The user-ratified surface is positionalentry +
--params+ the topology hash, not a pip override; a pip override is anadditive follow-on if ever wanted, not a keystone need. The continuation plan
(
docs/plans/0092b-run-from-blueprint-cli.md, Tasks 4-5) carries this.