fa42bf38782f2a92192e8438c87c33feccb8dd47
560 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fa42bf3878 |
feat(0099): CLI exit-code split (iteration 2) — usage=2, runtime=1
Apply the clean exit-code partition (#175 deviation #8) on top of the clap migration: runtime failures now exit 1, usage errors stay exit 2, with no same-class inconsistency. Partition (attribution principle): exit 2 = a command-line fault (a bad flag value, an inapplicable combination, or the content of an argv-named file); exit 1 = the command was well-formed but the environment / recorded state it needs is missing, or piped stdin data is bad. The 6 boundary cases the spec's "parse-time vs run-time" rule under-specified are resolved on #175 (malformed/open argv blueprint -> usage 2; stdin op-script content -> runtime 1; applicability refusals -> usage 2; unknown --axis / --metric -> usage 2). Flipped 2->1 (runtime): no local data, no recorded geometry, no recorded run/family, "run has no tap named", trace-name collision, family/trace persist-write failures (normalizing the lone generalize inconsistency -- all persist -> 1 together), missing/corrupt content-addressed store state (the reproduce path), chart-read failures, and graph stdin op-script content + stdin-read I/O. reproduce-diverged stays exit 1. Usage sites (clap parse, argv-applicability guards, the dual-grammar blueprint-file read/parse) stay exit 2. Tests: ~8 domain-refusal pins flipped Some(2)->Some(1) (message substrings unchanged), the four runtime tests renamed _exit_2 -> _exit_1, and a partition property test added. Orchestrator fix after the loop: 3 sibling no-data skip-guards (run_real_with_trace + the two generalize cross-instrument tests) that the plan's Task-1 list missed -- left at Some(2), they would fall through to assert Some(0) and FAIL on a data-less machine; flipped to Some(1) for consistency with the flipped code. Deferred (cosmetic, filed forward): full "Usage:" / "usage:" / bare error-message casing normalization -- no functional impact, high pin-churn. Verified green (orchestrator, this session): cargo build --workspace, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace all clean (0 failed across every test binary). This completes the #175 clap-adoption cycle (iteration 1 = clap migration at 366170a; iteration 2 = this exit-code split). refs #175 |
||
|
|
ffb2624dd7 |
plan: 0099 CLI exit-code split (iteration 2)
Iteration-2 plan for #175: apply the clean exit-code partition (deviation #8) on top of the clap migration — runtime failures exit 1, usage errors exit 2, no same-class inconsistency. Uses the attribution principle (command-line fault = 2, missing-environment/bad-stdin = 1) to resolve the 6 boundary cases the spec under-specified. refs #175 |
||
|
|
366170aeab |
feat(0098): CLI clap migration (iteration 1) — scoped help, --version, GNU flags
Replace the hand-rolled aura-cli argv parser with a clap derive parser. Delivers, all from one declarative source: - scoped `aura <sub> --help` (stdout, exit 0) — each subcommand's own Options section; retires the #131 uniform-global-blob help - `--version` / `-V` → `aura 0.1.0` (stdout, exit 0) - GNU `--flag=value`, the `--` end-of-options terminator, and long-option abbreviation (`--harn` → `--harness`) Architecture: one root Cli/Command enum, one `*Cmd` struct per subcommand. The four dual-grammar subcommands (run/sweep/walkforward/mc) map the loaded-blueprint branch as an optional `[blueprint]` positional + a post-parse `is_file()` dispatch on a single-sourced predicate; each built-in handler re-asserts a stray-positional guard (refuse-don't-guess) so clap's optional positional cannot silently swallow a typo (pinned by two new E2E tests). The execution layer (run_*/emit_*/runs_*/reproduce_*) is untouched — only arg-plumbing changes, via thin `*_from` adapters reusing the existing value helpers. Exit codes behaviour-preserved: clap parse errors exit 2 (matching the old usage-error=2); domain refusals stay exit 2; reproduce-diverged stays exit 1. The exit-code split (2=usage / 1=runtime) is iteration 2. clap is admitted under the C16 per-case review (research-side CLI, invariant 8 untouched — a dev-loop compile tax, not a frozen-artifact tax). Forced plan corrections (implementer; verified against the diff): - Step 6's deletion of the `parse_*_args` fns orphaned ~34 grammar unit tests that called them; those were deleted and that grammar coverage moved to the renegotiated cli_run.rs E2E pins (`parse_select` kept + retested). - run_malformed_cost_value_...: a bad cost value is now a clap value-parse error (no "Usage:" line); renegotiated to pin exit 2 + empty stdout + the flag name "--cost-per-trade"; the units note stays pinned via `run --help`. - `allow_hyphen_values` on the three cost flags so `--cost-per-trade -0.5` reaches the non-negativity guard (else clap rejects -0.5 as an unknown option). - Deleted the now-dead `RealWindowGrammar` (clippy -D warnings). Orchestrator additions after the loop: - Enabled long-option abbreviation (`infer_long_args` on the root — one attribute, it propagates to subcommands) + a test, closing spec acceptance criterion 4 which the plan's RED tests did not cover. Decided to deliver rather than amend the spec: the two ratified decisions conflict on this low-tier item — Fork B (GNU compliance, getopt_long abbreviates) vs F5 (LLM/automation-first, human-only niceties deprioritized) — and the cost tiebreaker (one attribute, not per-struct work) resolves to deliver. Carry-on debt (not this iteration): the error-message "Usage:"/"usage:"/bare casing is now mixed (clap's capitalized "Usage:" beside preserved lowercase aura messages); a deliberate quality-hold since the casings are pinned by renegotiated/preserved tests. Cosmetic; a candidate for the iteration-2 exit-code-split cleanup. Verified green (orchestrator, this session): cargo build --workspace, cargo clippy --workspace --all-targets -- -D warnings, and cargo test --workspace all clean (0 failed across every test binary, including the renegotiated cli_run pins and the new --version / scoped-help / GNU-flag / dual-grammar tests). refs #175 |
||
|
|
215f403496 |
plan: 0098 clap migration (iteration 1)
Iteration-1 plan for #175: replace the hand-rolled aura-cli argv parser with a clap derive parser (scoped `aura <sub> --help`, `--version`/`-V`, per-flag Options section, `--flag=value`/`--`/long-opt abbreviation), behaviour-preserving on exit codes. Task 1 adds clap + RED tests for the new behaviour; Task 2 is the atomic migration + renegotiation of the help/usage pins the migration inverts (the four dual-grammar subcommands mapped as optional [blueprint] positional + post-parse is_file() dispatch; execution layer untouched). The exit-code split is iteration 2. refs #175 |
||
|
|
e2e3a51c6c |
spec: 0098 CLI GNU/clig.dev compliance via clap adoption (boss-signed)
Replace the hand-rolled aura-cli argv parser with a clap derive parser: scoped `aura <sub> --help`, `--version`/`-V`, a per-flag Options section, and `--flag=value`/`--`/long-opt abbreviation for free. Two iterations — (1) clap migration, behaviour-preserving on exit codes; (2) exit-code split (usage=2, runtime=1). clap is admitted under the C16 per-case review (research-side CLI, invariant 8 untouched). Forks ratified by the user (clap over hand-rolled; GNU-compliance over the machine-first reframe; exit-code Option A) and the three spec-time derived decisions (dual-grammar clap mapping, parse-vs-runtime exit classification, two-iteration split) are recorded on #175. Boss-signed on grounding-check PASS (independent, fresh-context; 13 current-behaviour assumptions all ratified by named green tests). refs #175 |
||
|
|
780d823067 |
fieldtest: World/C21 milestone — 4 examples, 9 findings (functional promise holds)
Milestone-close validation of the World/C21 blueprint-data family surface, run
from the PUBLIC interface only (design ledger + op-script grammar; blueprints
authored as a consumer via `aura graph build`, no crates/ source read). Binary
built from
|
||
|
|
4710fccf32 |
audit(0097): cycle close — IS-refit walk-forward over a loaded blueprint; ledger refreshed (C24/C18); rm ephemeral spec/plan
cycle 0097 tidy (clean). Architect drift review over 485f3c8..HEAD: no code drift, no debt. Regression: no dedicated script (architect is the sole gate); its "What holds" is non-empty (design commitments confirmed against the diff): - Inv. 8 / 9: the cycle touches only crates/aura-cli (main.rs + cli_run.rs + the ephemeral spec/plan); walk_forward/WindowRoller/FamilyKind::WalkForward/ append_family/put_blueprint are read-only reuse, no engine/registry change. - Inv. 1 / 2: blueprint_walkforward_family reuses the generic walk_forward roller (windows disjoint-parallel, across-sim); each IS-sweep/OOS-run sees only its window's windowed_sources; IS precedes OOS per window — no look-ahead. - Reproduce lockstep: the reproduce_family_in WalkForward arm rebuilds each OOS slice from stored.manifest.window (before the _ => run_sources fallthrough that would rebuild the wrong full-window source); winner params via the shared manifest->cells recovery; pinned by the reproduces_bit_identically E2E. - Structural mirror + panic-safety: one-substitution-deep twin of the hard-wired Stage1R arm (select_winner + walk_forward reused, metric sqn_normalized); run_blueprint_walkforward follows the content-addressed sibling pattern (no ensure_name_free); blueprint_sweep_over returns Result<_,BindError>, a bad axis is in-closure exit 2 (never a panic); run_oos_blueprint derives pip internally (7 args, no #[allow]). Ledger: refreshed the C24 realization block (docs/design/INDEX.md) — replaced the now-false "Walk-forward is deferred (#173, dep #169)" sentence with the "IS-refit walk-forward shipped (cycle 0097)" note; and extended the C18 reproduction note — reproduce now spans the third family kind (WalkForward), so all three FamilyKind variants persist AND reproduce through the one shared topology_hash+put_blueprint hook. Milestone: the World/C21 blueprint-data family surface (run/sweep/mc/walkforward/ list-axes/reproduce) is coherent and complete per the architect review — no structural gap. This audit proves drift-clean; a green end-to-end MILESTONE FIELDTEST remains the deliberate gate to formally close the World/C21 milestone. Ephemeral spec/plan git-rm'd per the lifecycle convention. refs #173 |
||
|
|
ff20f74e8f |
feat(0097): IS-refit walk-forward over a loaded blueprint — aura walkforward <bp.json> --axis
Ship the World/C21 milestone's last piece (Arm A, #173): a true in-sample-refit walk-forward over a loaded blueprint. `aura walkforward <blueprint.json> --axis <name>=<csv> [--axis …] [--select argmax|plateau:mean|plateau:worst]` rolls 24/12/12 IS/OOS windows over the synthetic span; per IS window it re-optimizes the blueprint's params over the user's --axis grid (the #169 prefixed names), selects the winner by sqn_normalized, runs it out-of-sample, and aggregates the per-window OOS reports into a FamilyKind::WalkForward family — content-addressed (put_blueprint) and aura-reproduce bit-identical. Structure mirrors the hard-wired stage1-r walk-forward arm one substitution deep: the generic engine walk_forward driver + select_winner + the synthetic schedule are reused; only the per-window IS-sweep (blueprint_sweep_over) and OOS-run (run_oos_blueprint) source the loaded blueprint via cycle-0096's blueprint_axis_probe. reproduce_family_in gains a WalkForward arm reconstructing each OOS window's windowed slice from the member manifest's window bounds (winner params via the shared manifest->cells recovery, as the MonteCarlo/Sweep arms do). Panic-safety: blueprint_sweep_over returns Result<_, BindError>; a bad/unknown --axis is a clean in-closure exit 2 (mirroring the hard-wired arm's select_winner handling), never a panic — pinned by the rejects_an_unknown_axis E2E. A no-axis invocation (nothing to re-fit) is a usage error; a malformed blueprint is rejected at the dispatch boundary (exit 2, no panic). Reduce-mode members carry R-metrics (no raw pip curve) → empty stitched equity; oos_r is the meaningful summary (C10 R-first yardstick). Held quality finding adjudicated: run_oos_blueprint's plan-prescribed redundant `pip: f64` (== data.pip_size()) removed — derived internally to match its siblings run_oos_r / blueprint_sweep_over, which also drops the too_many_arguments band-aid #[allow] (8->7 args). Two plan transcription typos the loop caught and fixed verified correct: BindError renders via Debug ({e:?}), parse_select via map_err (Result, not Option). Verified myself: cargo build --workspace, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace all green; 2 new unit tests + 5 new cli_run E2Es (persist shape, reproduce bit-identical, no-axis, malformed-safety, unknown-axis) pass; no regression in the hard-wired walkforward / sweep / mc / reproduce suites. Engine/registry untouched (invariants 1/2/8/9). closes #173 |
||
|
|
c8c34c72e7 |
plan: 0097 IS-refit walk-forward over a loaded blueprint
Four tasks, one iteration: (1) the windowed IS-sweep + OOS-run helpers + blueprint_walkforward_family (mirrors the hard-wired Stage1R walkforward arm one substitution deep — loaded blueprint via blueprint_axis_probe); (2) the --axis/--select parse grammar + IO wrapper (persist via put_blueprint + append_family, content-addressed like aura mc) + the .json walkforward dispatch arm; (3) the reproduce_family_in WalkForward branch (persist+reproduce land together); (4) the E2Es (persist shape, reproduce bit-identical, no-axis reject, malformed-safety) + the full clippy/suite gate. Panic-safety: blueprint_sweep_over returns Result<_, BindError> (user --axis fallible); a bad axis → clean in-closure exit 2 (mirrors the hard-wired arm's select_winner handling), never a panic. reduce-mode members carry R-metrics (no raw pip curve) → empty stitched equity, oos_r is the meaningful summary (C10 R-first). Closes the World/C21 milestone's last piece. refs #173 |
||
|
|
1e2ac82473 |
spec: 0097 IS-refit walk-forward over a loaded blueprint (boss-signed)
Add `aura walkforward <blueprint.json> --axis <name>=<csv> [--axis …]`: a true in-sample-refit walk-forward over a loaded blueprint (Arm A, #173). Per IS window it re-optimizes the blueprint's params over the user's --axis grid, selects the winner by sqn_normalized, runs it out-of-sample, and aggregates the per-window OOS reports into a FamilyKind::WalkForward family that is content-addressed (put_blueprint) and aura-reproduce-aware. Closes the World/C21 milestone's last piece (the walk-forward half of #170). Design (Arm A settled by the user; sub-forks derived and recorded on #173): the loaded-blueprint WF mirrors the hard-wired stage1-r walk-forward arm one substitution deep — the generic engine walk_forward driver + select_winner + the 24/12/12 synthetic schedule are reused verbatim; only the per-window IS-sweep and OOS-run source the loaded blueprint (wrap_stage1r via cycle-0096's blueprint_axis_probe) over the user --axis grid. Persist + reproduce ship together (coupled — a persisted family with no store hook is unreproducible, the #170 hazard); the reproduce WalkForward branch reconstructs each OOS window's windowed source + winner params from the member manifest, mirroring the 0095 MonteCarlo branch. Loaded members are reduce-mode (R-metrics, no raw pip curve), so the WF is measured in R (oos_r), stitched pip-equity empty — consistent with the C10 R-first yardstick. Boss-signed on grounding-check PASS (independent, fresh-context; all 8 load-bearing current-behaviour assumptions ratified by named green tests — incl. an empirically-confirmed one: a loaded-blueprint member carries and ranks by sqn_normalized. One spec-internal tension flagged + fixed before sign). Engine/registry untouched (invariants 1/2/8/9). refs #173 |
||
|
|
485f3c8c89 |
audit(0096): cycle close — axis discovery over a loaded blueprint; ledger refreshed (C24); rm ephemeral spec/plan
cycle 0096 tidy (clean). Architect drift review over ebebc24..HEAD: no drift, no debt. Regression: no dedicated script (architect is the sole gate); its "What holds" is non-empty (design commitments confirmed against the diff + a green test run, not the commit body): - Inv. 8 / 9: the cycle touches only crates/aura-cli (main.rs + cli_run.rs + the ephemeral spec/plan) — no engine/registry/blueprint.rs change; those are read-only callees. - Inv. 1: --list-axes is a pure read-only probe (short-circuits before run_blueprint_sweep; builds the wrapped composite, reads param_space, prints — no run, no seed, no engine execution). - Behaviour-preserving single-source: blueprint_axis_probe is token-identical to the two former inline probes; three copies (sweep/mc/list) collapse to one; the reload closures survive (topology_hash + per-member run); clippy -D warnings clean, no dead code; the subsumed tests (blueprint_sweep_member_equals_single_run_and_shares_topology_hash, blueprint_mc_family_rejects_an_open_blueprint) stayed green. - The blueprint_axis_probe .expect is not a latent panic: the ["sweep",..] arm validates blueprint_from_json first (exit 2), pinned by the shipped aura_sweep_list_axes_rejects_malformed_blueprint E2E (no panic). Ledger: refreshed the C24 realization block (docs/design/INDEX.md) — reclassified the "axis-name discovery surface" from tracked debt to shipped (cycle 0096), recording the single-sourced blueprint_axis_probe and the listed==swept-by- construction property. Per-member trace-writing (#168) remains the one tracked debt in that block. Milestone World/C21 stays OPEN — the walk-forward half (#173, IS-refit, now unblocked by this #169) remains. Ephemeral spec/plan git-rm'd per the lifecycle convention. refs #169 |
||
|
|
bd2003e217 |
feat(0096): list a loaded blueprint's sweepable axes — aura sweep <bp.json> --list-axes
A data-level author can now discover a loaded blueprint's open sweepable knobs before sweeping: `aura sweep <blueprint.json> --list-axes` prints one `<name>:<kind>` line per open knob, in param_space() order, then exits 0. Before this the only way to learn the names was to guess a `--axis` and read the MissingKnob/UnknownKnob error — which names only the rejected knob, never the valid set. Prerequisite for #173 (IS-refit walk-forward must know which axes to re-fit per in-sample window). Design (derived, recorded on #169): the query lands on the SWEEP verb, not `graph introspect`, because the accepted axis names are produced by the sweep's own harness-wrapping (wrap_stage1r nests the loaded signal as a BlueprintNode::Composite named "stage1_signal", so collect_params prefixes them — stage1_signal.fast.length, not the raw fast.length). Only the sweep verb owns that wrapping. A new blueprint_axis_probe helper single-sources the wrapped probe and now subsumes the two previously-inline copies (blueprint_sweep_family + blueprint_mc_family), so listed == swept by construction (and stays so across #159's harness retirement) — no second source of truth. --list-axes is a standalone query (valueless flag handled before the per-flag value-consume; rejected with exit 2 if combined with any other flag). A closed blueprint prints nothing (exit 0). A malformed blueprint is rejected at the existing dispatch boundary (exit 2, UnknownNodeType), never a panic. Tests: unit blueprint_axis_probe (prefixed names + empty for closed) + unit parse grammar (standalone), and five cli_run E2Es — prints names, closed-empty, rejects-other-flags, malformed-safety (no panic), and a round-trip that DERIVES the names from --list-axes and feeds them back into a working sweep (pins the listed==swept invariant #169 exists to close). Verified myself: cargo build --workspace, cargo test --workspace, cargo clippy --workspace --all-targets -D warnings all green; no regression in the refactored sweep/mc probes. Engine and registry untouched (invariants 1/8/9). closes #169 |
||
|
|
581c86b091 |
plan: 0096 discover a loaded blueprint's sweepable axes
One iteration, three tasks: (1) extract blueprint_axis_probe and subsume the two existing inline copies of the wrapped-namespace probe (blueprint_sweep_family + blueprint_mc_family) into it — the single source the spec's rationale requires; (2) the --list-axes surface (SweepBlueprintArgs field, valueless-flag parse + standalone guard, list_blueprint_axes, dispatch short-circuit); (3) four E2Es (open lists prefixed names, closed empty, combined-flags reject, malformed-blueprint safety — no panic). refs #169 |
||
|
|
a78a0557f4 |
spec: 0096 discover a loaded blueprint's sweepable axes (boss-signed)
Add `aura sweep <blueprint.json> --list-axes`: a read-only query sub-mode that lists a loaded blueprint's open sweepable knobs (one `<name>:<kind>` per line, param_space() order), then exits. Closes the discovery gap #169 surfaced — today the only way to learn the axis names is to guess one and read a MissingKnob/UnknownKnob error, and that error names only the rejected knob, never the valid set. Prerequisite for #173 (IS-refit walk-forward must know which axes to re-fit per in-sample window). Derived surface decision (recorded on #169): the query lands on the SWEEP verb, not `graph introspect`. The axis names `--axis` accepts are produced by the sweep's own harness-wrapping — the loaded signal (name "stage1_signal") is nested via wrap_stage1r as a BlueprintNode::Composite, so collect_params prefixes the names (stage1_signal.fast.length), NOT the raw param_space (fast.length; empirically: the bare name → UnknownKnob). Only the sweep verb, which owns the wrapping, can print the names the user will actually pass; a shared blueprint_axis_probe single-sources the probe for both the sweep terminal and --list-axes, so listed == swept by construction (and stays correct across #159's harness retirement). Boss-signed on grounding-check PASS (independent, fresh-context; one BLOCK raised + fixed: the malformed-sweep .expect panic-path is now pinned by a this-cycle E2E over unknown_node.json, not an untested current-behaviour claim). Engine/registry untouched (invariants 1/8/9). refs #169 |
||
|
|
ebebc2401b |
audit(0095): cycle close — MC over a loaded blueprint; ledger refreshed (C18/C21); rm ephemeral spec/plan
Architect drift review (scope cec7811..HEAD, the sole gate — no regression script) returned drift-clean with positive What-holds: - invariants 8/9: the whole cycle is aura-cli (main.rs + cli_run.rs + ephemeral docs); nothing entered aura-engine/aura-registry; FamilyKind::MonteCarlo, monte_carlo, append_family, mc_member_reports are pre-existing seams reused. - invariant 1 / C1: draws run disjoint-parallel via the engine monte_carlo seam (seed-input order, no within-sim concurrency); bit-identity proven by the reproduce round-trip (unit + E2E). - the C1 lockstep is a single shared fn: synthetic_walk_sources (one fixed SyntheticSpec) is called by BOTH the persist path and the reproduce MonteCarlo branch; pip is lockstepped too (both pass DataSource::Synthetic). - the sweep-sibling pattern matches: Result<McFamily,String> + boundary render. - scope honesty: WF deferral (#173, dep #169) and synthetic-walk-DGP (#172) are named in the spec + feat body, not silent. Carry-on debt (does NOT block close, filed forward, NOT new-this-cycle drift): synthetic_walk_sources' len:60 is hand-calibrated to the shallow stage1 warm-up; a deep-lookback closed blueprint warms poorly -> silent-vacuous draws with no error, and the anti-degenerate test only exercises the shallow fixture. Folded into #172 (real-data DGP retires the length calibration) per the architect's recommendation, rather than a new thread. Ledger refreshed: the C21 realization block now records the MC verb shipped (cycle 0095) with WF deferred; the C18 reproduction block notes aura reproduce now spans FamilyKind::MonteCarlo. Verified green (orchestrator, this session): cargo build --workspace clean; cargo clippy --workspace --all-targets -- -D warnings clean; cargo test --workspace green. Cycle is drift-clean (not a milestone close — #170's WF half remains, milestone stays open). refs #170 |
||
|
|
14e27a978b |
feat(0095): monte-carlo over a loaded blueprint — aura mc <bp.json> --seeds N
The MC half of #170 (World/C21): `aura mc <closed blueprint.json> --seeds N` builds a FamilyKind::MonteCarlo family from a loaded CLOSED blueprint. Each seed draws a distinct synthetic price walk (synthetic_walk_sources, the mc_family pattern), so members differ in the DATA (not just a manifest label) — the sim is deterministic (C1), so MC variation must come from re-drawing the input. Draws run disjointly in parallel via the shared monte_carlo helper (invariant 1). The canonical blueprint is stored once keyed by the shared topology_hash (the 0094 hook), and reproduce_family_in gains a MonteCarlo realization branch that reconstructs each member's seed-driven walk from manifest.seed — so a persisted mc family 'aura reproduce's bit-identically (the store-hook proof). Closed-blueprint only: MC binds no axis, so a free knob has no binder; an open blueprint yields a named Err (rendered to stderr + exit 2 at the run_blueprint_mc boundary, mirroring blueprint_sweep_family — no hidden process exit in the pure builder), pre-empting the compile_with_params arity panic. Two quality-gate-driven improvements over the ratified plan, both applied and verified: (1) delegate the disjoint draws to the engine monte_carlo seam instead of a hand-rolled loop (my plan's 'closure-shape mismatch' rationale was wrong — the Fn(u64,&[Scalar]) closure ignores the unused base_point, exactly as mc_family does); (2) the closed-blueprint guard returns Result rather than exiting inside the builder, matching the sweep sibling's contract and making the rejection unit-testable. Engine + registry untouched (invariants 8/9). Verified: cargo build --workspace clean; cargo clippy --workspace --all-targets -- -D warnings clean; cargo test --workspace green (5 new tests: the parser, anti-degenerate family, MC reproduce round-trip, and two E2Es incl. the open rejection; no regressions). refs #170 |
||
|
|
860b89ad93 |
plan: 0095 monte-carlo over a loaded blueprint
4 tasks: parse_mc_blueprint_args, blueprint_mc_family (closed-blueprint guard + seed-driven synthetic walks), the reproduce MonteCarlo realization branch, and run_blueprint_mc + the .json dispatch arm + E2E. All in aura-cli/main.rs (engine/registry untouched). |
||
|
|
7116660fe8 |
spec: 0095 monte-carlo over a loaded blueprint (boss-signed)
The MC half of #170 (World/C21): aura mc <closed blueprint.json> --seeds N builds a FamilyKind::MonteCarlo family from a loaded closed blueprint, each seed drawing a distinct synthetic price walk (the mc_family pattern), sharing one topology_hash, written once to the content-addressed store (the 0094 hook) so aura reproduce re-derives it bit-identically. Scope narrowed from #170's two verbs by two read-only review gates: - spec-skeptic: a loaded-blueprint 'walkforward' with no in-sample refit under the verb name that hard-wired walkforward uses for real IS-optimization is a guarantee collision -> walk-forward deferred to #173 (deps #169 axis-discovery). - grounding-check: there is no per-knob declared-default mechanism and the raw bind path panics on arity mismatch -> MC runs a CLOSED blueprint over the empty point (as aura run does), rejecting an open blueprint with a named error + exit 2. Grounding-check PASS on the revised spec; decisions logged on #170. refs #170 |
||
|
|
cec7811c32 |
audit(0094): cycle close — content-addressed reproduction; ledger refreshed, coverage closed; rm ephemeral spec/plan
Architect drift review (4751d4b..HEAD): drift_found — ledger-currency only; the
code is clean.
What holds (architect, confirmed by diff + green tests):
- Inv 8 / C9: aura-engine byte-untouched; content_id + sha2 in aura-cli, the
store in aura-registry.
- Inv 9: Registry::{put,get}_blueprint is a dumb bytes-by-key store (opaque
String keyed by String hash; no sha2, no parse, no sha256==hash verify — the
caller owns the hash). No domain knowledge crept into the engine or registry.
- C1: the run_blueprint_member extraction is behaviour-preserving (keystone
blueprint_sweep_member_equals_single_run_and_shares_topology_hash unchanged +
green); serde_json/float_roundtrip serves determinism (disk==recomputed), not a
mask (1-ULP canary green).
- C11/C12: one blueprint stored per family, keyed by the shared topology_hash.
Resolutions:
- [ledger] Refreshed C18 + C24: added the cycle-0094 Realization (the
content-addressed store runs/blueprints/<hash>.json + aura reproduce), and
retired the now-false "content-addressed identity / full reproduction deferred"
claims (INDEX.md:1261, C24 deferred block). Only the structural-axis /
whole-harness content-id (and the debug-name-in-id question) genuinely remains,
filed forward (#171, #170).
- [debt->closed] Added the two untested refuse-don't-guess E2E arms
(aura_reproduce_rejects_a_non_generated_family — a hard-wired sweep member has
no topology_hash; aura_reproduce_rejects_a_missing_stored_blueprint — the store
was deleted), and a cross-surface unit pin
(topology_hash_is_the_content_id_of_the_canonical_form) so the two acc-1 paths
cannot silently drift.
- [scaling] MC/walk-forward-from-blueprint (#170) will need the same put_blueprint
hook or reproduce won't cover them — noted on #170.
No regression scripts in this project (Test is the gate): full workspace suite
green (51 suites), clippy -D warnings clean, binary verified end-to-end.
Ephemeral spec/plan git-rm'd per the lifecycle convention; durable rationale is in
the ledger. The World/C21 milestone stays open (#170 remains).
closes #158
|
||
|
|
717a0b70af |
feat(0094b): content-id surface + Tier-1 stability — aura graph introspect --content-id (iter 2)
Completes #158 acceptance (acc 1 + acc 3) on top of iteration 1's reproduction: - acc 1: `aura graph introspect --content-id` reads an op-script and prints the content id of its canonical blueprint — the SHA256 (hex) of the same blueprint_to_json bytes `graph build` emits. Extracted the single `content_id` primitive shared by this surface AND topology_hash, so the two command paths (this surface / hashing a `graph build` output) agree by construction. Verified live: both e2be81b2… for the SMA-cross op-script. Deterministic + distinguishes topologies; a malformed op-list fails cleanly (exit 2, no partial hash). - acc 3: a Tier-1 optional field the blueprint does not use is tolerated by the loader (#156) and absent from the canonical omit-defaults form, so the content id is unchanged (content_id_is_stable_across_a_tolerated_tier1_field); plus content-id stability across the serialize -> reload -> re-serialize store round-trip (#164), the property reproduction rests on. Finding (documented, filed forward): an op-script and the Rust `stage1_signal` builder produce DIFFERENT canonical forms — the composite debug-name ("graph" vs "stage1_signal"), a named vs unnamed Sub, and an unbound vs bound Bias.scale — so they are different topologies by the byte definition and get different content ids. Content-addressing keys on the canonical form, which currently includes the composite debug-name (a non-load-bearing symbol, invariant 11). Whether the content id should exclude the debug-name (a topology-canonical form distinct from the byte-canonical form, which would also change the shipped cycle-0092 topology_hash values) is a forward design question, not this cycle. Verified: full workspace suite green (51 suites); clippy -D warnings clean. refs #158 |
||
|
|
008692c043 |
feat(0094): content-addressed reproduction — aura reproduce <family-id> (iter 1)
Restore C18 reproducibility for generated (blueprint-sweep) runs (#158, acc 2): a sweep's topology is stored once, content-addressed by the topology_hash the manifest already carries, and `aura reproduce <family-id>` re-derives every persisted member bit-identically (C1) from the stored blueprint. - aura-registry: a dumb content-addressed bytes store — Registry::put_blueprint / get_blueprint / blueprints_dir over runs/blueprints/<hash>.json. No sha2 dep (caller owns the hash; reproduction's bit-identical compare is the integrity check); absent id -> Ok(None) (treat-as-empty, like load). - aura-cli: run_blueprint_member extracted from blueprint_sweep_family's member closure (behaviour-preserving; keystone stays green) so the sweep AND reproduce re-run the identical reduce-mode path — bit-identity by construction. The store write hooks the sweep persist seam (one blueprint per family, shared topo). reproduce_family_in loads each member's blueprint by hash, reconstructs the point from the recorded params (point_from_params, inverse of zip_params, over the WRAPPED signal's param_space so the prefixed knob names match), re-runs, and compares metrics. `["reproduce", id]` dispatch arm; refuse-don't-guess on an unknown id / missing stored blueprint (exit 2), DIVERGED -> exit 1. Dependency decision (per-case review, ratified): enabled serde_json `float_roundtrip`. Stored member metrics round-trip through families.jsonl as f64 JSON; the default parser can be 1 ULP off, which would make disk-loaded != recomputed and "bit-identical" (C1) physically impossible. The feature guarantees exact f64 round-trip. Blast-radius clean (full workspace suite green); it serves determinism, the invariant reproduction rests on. Canary: f64_blueprint_param_survives_store_round_trip_bit_identically. Two plan bugs caught + fixed by the implement loop and verified: reproduce's param_space must come from the wrapped signal (prefixed names, the #167 lockstep), and the family id is 0-indexed (live-0 / smacross-0, not -1). Verified: full workspace suite green (51 suites); clippy -D warnings clean; and the binary end-to-end — `aura sweep` (3 members, one shared stored blueprint) then `aura reproduce live-0` (3/3 bit-identical incl the open-at-end member), unknown id -> exit 2. acc 1 (graph introspect --content-id) + acc 3 (Tier-1 id stability) land in iteration 2 (plan 0094b). refs #158 |
||
|
|
a6d3774d04 |
plan: 0094 content-addressed reproduction (iteration 1)
Iteration 1 (acceptance 2, the heart): the content-addressed blueprint store (Registry::put_blueprint/get_blueprint under runs/blueprints/), the store write at the sweep persist seam, the extracted shared run_blueprint_member, and `aura reproduce <family-id>` re-deriving every persisted member bit-identically from the stored blueprint. acceptance 1 (graph introspect --content-id) and 3 (Tier-1 id stability) deferred to plan 0094b. refs #158 |
||
|
|
4b6bc52794 |
spec: 0094 content-addressed reproduction (boss-signed)
Restore C18 reproducibility for generated (blueprint-sweep) runs. Since cycle 0093 a sweep member's topology is a generated data value, so commit alone no longer identifies the graph and the manifest is not a complete re-derivation recipe (#158). Design: content-address the topology. The canonical blueprint JSON is stored once, keyed by the topology_hash the manifest already carries (cycle 0092), under runs/blueprints/<hash>.json (a dumb bytes-by-key store in aura-registry, no sha2 dep — the reproduce path's bit-identical compare is the integrity check). `aura reproduce <family-id>` loads each member's blueprint by hash, re-bootstraps with the member's params/window/seed, re-runs, and asserts the metrics are bit-identical (C1). acc-1 (two paths -> same content id) via a minimal `graph introspect --content-id`; acc-3 (Tier-1-stable id) composes the #156 omit-defaults pin. Derived forks (logged on #158): content-address (not carry-inline) — keeps the manifest tiny (C18) and dedups one topology across a family (C11/C12); hash domain = signal-only this cycle (the scaffolding is not yet blueprint-data, C24), boundary documented, whole-harness id deferred (#156/#159). Engine untouched (C9); store + hash off the frozen engine (invariant 8). boss-signed on a grounding-check PASS (9/9 current-behaviour assumptions ratified by green tests). refs #158 |
||
|
|
4751d4b590 |
audit(0093): cycle close — sweep-from-blueprint; ledger refreshed, debt filed; rm ephemeral spec/plan
Cycle-close drift review (architect) for cycle 0093 / #166 (aura sweep <blueprint.json>). Drift-found on debt only; no contract violation. What holds (architect, evidence of review): invariant 9 fail-clean (resolve_axes runs before the sweep closure → named BindError UnknownKnob/MissingKnob/KindMismatch, exit 2; UnknownNodeType on load; no registry); C1/C12/C18 shape verbatim from stage1_r_sweep_family (members disjoint + enumeration-ordered; shared topology_hash correct, consistent with the cycle-1 #158 signal-only scope; append_family/ sweep_member_reports reused); reload-per-member is sound (Composite !Clone, #164); stdout uses family_member_line with family_id, run_sweep-consistent. Resolved: ledger refreshed (C24 Status — the cycle-0093 realization: the World now orchestrates FAMILIES from blueprint-data; the open-vs-bound fixture fact + the reload-per-member rationale recorded). Drift filed forward: #168 (--trace writes no per-member traces — silently like --name, debt-med), #169 (no axis-name discovery for loaded sweeps, debt-low), #170 (MC/walk-forward over a loaded blueprint — the next family-verb cycle, assigned to the milestone). Ephemeral 0093 spec + plan git-rm'd per the lifecycle convention. closes #166 |
||
|
|
481add0ca2 |
feat(0093): sweep-from-blueprint — aura sweep <blueprint.json> over named axes
Cycle 2 of the World/C21 milestone (#166): the World now orchestrates FAMILIES from blueprint-data. `aura sweep <blueprint.json> --axis <name>=<csv> …` loads an OPEN signal blueprint, grids the by-name axes against its param_space, builds each member via cycle-1's wrap_stage1r seam, and aggregates to a FamilyKind::Sweep family — byte-identical in shape to the hard-wired sweep. - `parse_sweep_blueprint_args` + `SweepBlueprintArgs`: repeatable `--axis name=csv` (lexed i64-then-f64; resolve_axes kind-checks), `--name`/`--trace`, the shared `--real/--from/--to` accumulator → DataChoice. - `blueprint_sweep_family` (pure) + `run_blueprint_sweep` (IO) mirror stage1_r_sweep_family with three deviations: by-name axes (verbatim param_space names, e.g. `stage1_signal.fast.length`), reload-per-member (Composite is !Clone — its build closures are Box<dyn Fn>, #164 — so a fresh signal is re-loaded from the doc per member), and a per-member shared topology_hash. Members share the hash (same signal topology, only params vary); member_key distinguishes them. - The `aura sweep <file.json>` `.json`-discriminator arm mirrors cycle 1; the hard-wired `--strategy` sweep is untouched (#159). Task-3 design gap resolved: a sweep needs an OPEN blueprint (open knobs to grid), distinct from cycle-1's fully-bound run fixture (a bound blueprint has an empty param_space). New `tests/fixtures/stage1_signal_open.json` = blueprint_to_json(stage1_signal(None,None)); a sweep must bind ALL open knobs, so the E2E grids fast×slow (2×2 = 4 members). Verified end-to-end by running the binary: `aura sweep stage1_signal_open.json --axis stage1_signal.fast.length=2,4 --axis stage1_signal.slow.length=8,16 --trace f` persists a discoverable 4-member Sweep family; an unknown axis fails clean. Member equivalence vs run_signal_stage1r + shared-hash/distinct-member_key unit-tested. Full workspace suite green; clippy --all-targets -D warnings clean. refs #166 |
||
|
|
b6d4ebfe23 |
plan: 0093 sweep-from-blueprint
Three-task decomposition of #166 (sweep a loaded blueprint): the parser (parse_sweep_blueprint_args + SweepBlueprintArgs + --axis i64-then-f64 lexing), the member-build (run_blueprint_sweep — a structural mirror of stage1_r_sweep_family with three deviations: by-name axes, reload-per-member since Composite is !Clone, per-member topology_hash) + the .json dispatch arm, and the tests (grammar + equivalence-vs-run_signal_stage1r + the binary sweep-family E2E + unknown-axis fail-clean). Purely additive; reuses the engine sweep machinery + cycle-1's wrap_stage1r unchanged. Grounding decisions (Q1-Q5) recorded on #166. refs #166 |
||
|
|
132dee2437 |
spec: 0093 sweep-from-blueprint (boss-signed)
Cycle 2 of the World/C21 milestone (#166): `aura sweep <blueprint.json> --axis <name>=<csv>` sweeps a loaded signal over its named param-space axes, reusing the existing stage1-r sweep machinery with the signal source swapped to cycle-1's wrap_stage1r seam, aggregating to a FamilyKind::Sweep family; each member manifest carries the shared topology_hash. Derivable extension, not a fork (recon + grounding confirm): the sweep is by-name param-space-driven (SweepBinder.axis / resolve_axes / bootstrap_with_cells), so a loaded blueprint's param_space slots in; wrap_stage1r nests an arbitrary signal; append_family is blueprint-agnostic. Scoped to sweep (the keystone family); MC / walk-forward over a loaded blueprint are the same machinery per verb and follow as a fast follow-on. Decisions recorded on #166. Boss-signed on a grounding-check PASS: all six load-bearing current-behaviour assumptions ratified by named green tests (wrap_stage1r arbitrary-signal nesting, the by-name param-space sweep, stage1_r_sweep_family, append_family/FamilyKind::Sweep, topology_hash, the .json-discriminator dispatch precedent). refs #166 |
||
|
|
327d71b1d4 |
audit(0092): cycle close — run-from-blueprint; ledger refreshed, drift filed; rm ephemeral spec/plan
Cycle-close drift review (architect) for cycle 0092 / #165 (aura run <blueprint.json>). Drift-found on ledger currency only; the code is clean and correct. What holds (architect, evidence of review): the stage1_signal/wrap_stage1r split is behaviour-preserving (golden metrics byte-unchanged; loaded_signal bit-identical green; the STAGE1_R_SMA_* constants keep the hashed signal == the executed signal); sha2 + the topology_hash helper live in aura-cli only, aura-engine stays sha2-free (invariant 8 / C16); the resolver resolves the closed std_vocabulary set, unknown type -> UnknownNodeType, no registry (invariant 9); topology_hash is a correct Tier-1 optional and the RunManifest <-> RunManifestRead read-mirror both carry it. Resolved: ledger refreshed (C24 Status — the cycle-0092 run-from-blueprint realization + the topology_hash #158 anchor landing; #158 reframed from "premature" to "field landed, full reproduction remains"). Drift filed forward: #167 (single-source the stage1-r suffix-remapping lockstep — debt-med), and a scope note on #158 (the hash currently covers the signal only; a structural-variant content-id is the #158/#166 concern). The spec's illustrative instrument:"GER40" example (note-low) is moot — the ephemeral spec is removed here. Ephemeral 0092 spec + 0092/0092b plans git-rm'd per the lifecycle convention. closes #165 |
||
|
|
2b1c24668d |
test(0092): run-from-blueprint E2E + bit-identical keystone + demo fixtures
Closes the cycle-1 test coverage for #165. The `aura run <blueprint.json>` CLI arm itself (the .json dispatch + parse_blueprint_run_args + BlueprintRunArgs) shipped in ff4a1b3; this adds the coverage that proves it and the in-repo demo blueprint: - crates/aura-cli/tests/fixtures/stage1_signal.json — the canonical SMA-cross signal (blueprint_to_json of stage1_signal(2,4)); emit_demo_signal_fixture (#[ignore]) regenerates it. - crates/aura-cli/tests/fixtures/unknown_node.json — one type set to "Nope" for the fail-clean guard. - loaded_signal_runs_bit_identical_to_rust_built (the keystone, C1): a signal serialized -> loaded via blueprint_from_json -> run is bit-identical to its Rust-built twin, incl. topology_hash (64-hex). - topology_hash_is_stable_and_distinguishes (#158 anchor property). - cli_run.rs E2E: `aura run <demo>` exits 0 with topology_hash in the manifest; an unknown-node blueprint fails clean with UnknownNodeType (the #160 closed-set guard at the data-plane face, invariant 9). Verified end-to-end by actually running the binary: `aura run stage1_signal.json` -> RunReport with a 64-hex topology_hash, sim-optimal+risk-executor broker, R metrics; `aura run unknown_node.json` -> UnknownNodeType("Nope"), exit 2. Full workspace suite green; clippy --all-targets -D warnings clean. refs #165 |
||
|
|
43a25547ce |
plan: 0092b run-from-blueprint CLI arm + tests (continuation)
The remaining Tasks 4-5 of #165 after the
|
||
|
|
ff4a1b3d4a |
feat(0092): run-from-blueprint infrastructure — restructure + topology_hash + shared seam (Tasks 1-3)
Cycle-1 infrastructure for #165 (World/C21). Behaviour-preserving + workspace green; the CLI arm + tests (Tasks 4-5) follow. Task 1 — restructured stage1_r_graph into stage1_signal() (the serializable signal leg: SMA-cross -> Bias, a `price` input-role + a `bias` output) + wrap_stage1r(signal, ...) (broker/sinks/exec/cost around a nested signal), with stage1_r_graph() = wrap_stage1r(stage1_signal(...)). DEVIATION from the plan's "callers untouched" claim, verified behaviour-preserving: nesting the signal prefixes its param-space names (stage1_signal.fast.length), which broke the sweep's bare .axis("fast.length"); fixed exactly as the #137 stop-knob machinery does — FAST_LENGTH_SUFFIX/SLOW_LENGTH_SUFFIX suffix-resolution + stage1_r_friendly_name arms mapping the prefixed names back to the bare manifest names. All observable behaviour (manifest names, member keys, metrics, traces) byte-identical; the flat graph and every metric unchanged (full suite green, incl. sweep/walkforward/MC). Task 2 — RunManifest.topology_hash: Option<String> (Tier-1 optional, serde default/skip — old manifests byte-identical, #156), threaded None into all 24 literal sites across 7 crates. Also fixed the aura-registry RunManifestRead read-mirror to carry topology_hash (it was hardcoding None on load) — else a stored hash would silently drop on the registry round-trip once Task 3 stores a real one. Task 3 — the shared run_signal_stage1r seam (hash the signal, wrap, compile with params, bootstrap, run, manifest with params from param_space + topology_hash) + the sha256_hex topology_hash helper (sha2 in aura-cli, off the frozen engine, invariant 8). run_stage1_r now carries its topology_hash too (every run becomes self-identifying, #158). The seam is unwired until Task 4 (transient #[allow(dead_code)], retired there); RED-first seam tests added. Verified: cargo test --workspace green (51 suites, 0 failures); cargo clippy --workspace --all-targets -D warnings clean. refs #165 |
||
|
|
e25ca15be4 |
plan: 0092 run-from-blueprint
Five-task decomposition of #165 (run a serialized signal blueprint): 1. behaviour-preserving restructure of stage1_r_graph into stage1_signal() + wrap_stage1r() (the serializable signal boundary + the shared wrapping), gated by the stage1-r + bit-identical-e2e tests staying green; 2. RunManifest.topology_hash (Tier-1 optional) + thread None into the ~24 workspace literal sites (one compile-gate); 3. the shared run_signal_stage1r seam + the sha256_hex helper (sha2 in aura-cli, off the frozen engine, invariant 8) + run_stage1_r gains its topology_hash; 4. the `aura run <blueprint.json>` CLI arm (.json discriminator, --params cells), leaving the existing harness-kind dispatch intact (#159 stays); 5. demo signal blueprint + the bit-identical / determinism / E2E tests. Tasks 1-2 (restructure + field-add) run and verify first. Derived grounding decisions (the restructure target, param_space-derived manifest.params, the Q4 behaviour-preserving semantics) recorded on #165. refs #165 |
||
|
|
29a519dbf6 |
spec: 0092 run-from-blueprint (boss-signed)
Cycle 1 of the World/C21 milestone (#165): `aura run <blueprint.json>` loads a serialized C24 signal blueprint, wraps it in the existing Stage-1-R run scaffolding via a shared seam extracted from run_stage1_r, compiles + bootstraps + runs, and emits a RunReport whose RunManifest carries a new Tier-1-optional topology_hash (SHA256 of the canonical blueprint form, #158 anchor). A serialized blueprint is a SIGNAL (sinks/brokers/data/clock out of the round-trippable set, C24), so aura run WRAPS the loaded signal in a runnable harness — the same wrap the Rust path does, shared as one function so "bit-identical to the Rust path" is the same seam over the same signal. The round-trip bit-identity is already proven (blueprint_serde_e2e.rs); cycle 1 lifts it to the CLI + adds topology_hash. Boss-signed on a grounding-check PASS: all six load-bearing current-behaviour assumptions ratified by named green tests (blueprint_from_json, the bit-identical round-trip, the run_stage1_r compile/bootstrap/run path, RunManifest's Tier-1 optional-field pattern, canonical blueprint_to_json, window_of). Derived wrapping decision recorded on #165. sha2 enters under the C16 per-case dependency review (SHA256 user-settled). refs #165 |
||
|
|
df228986c4 |
docs(ledger): C24 enforcement shift — invariant 9 moves from structural to resolver-seam
A cycle-0091 design analysis (adversarially verified) of "does invariant 9 survive lifting blueprints onto the data plane?" records a load-bearing finding in the C24 entry. Pre-C24 a blueprint referencing another project's node was a compile error (topology was in-process Rust — structurally impossible). Post-C24 it is a type-id string refused only by the injected resolver's closed set (UnknownNodeType). The anti-NIH rationale survives (logic stays cargo+Gitea-only; the format distributes topology-as-content, never logic, C17) and std_vocabulary stays a compiled-in dispatch table, not a registry — but enforcement of the engine/project boundary degrades from compiler-guaranteed to resolver-seam discipline. Two consequences recorded: #160 becomes the data-plane face of that boundary (no longer droppable; its own drift still fails safe), and the injected per-project resolver is the genuinely new, deferred invariant-9 surface (a project can express a marketplace one layer above the engine contract) — a World/C21-layer charter point. Invariant 8 (frozen) is reframed alongside: runtime topology generation (research plane) + frozen deploy artifact + deterministic-once-instantiated (C1). refs #160 |
||
|
|
4a3ae05db2 |
test(graph_model): cover a producer backing >1 re-exported output field
#47 — close a coverage gap in the graph model serializer: a single interior producer node backing more than one re-exported output field was never exercised by a fixture (every existing one maps each output to a distinct producer or a single field). The issue was filed at cycle 0022 against `aura-cli/src/graph.rs`'s `render_definition` / `output_binding` helper, which rendered a textual `(n1, n2) := <producer>` tuple binding. That file and helper no longer exist — the textual definition renderer was replaced by the JSON model emission now in `aura-engine/src/graph_model.rs`, where the multi-OutField case is plain flat iteration over `c.output()` (one `[name,kind]` entry + one `#N` boundary binding per `OutField`, by `(node, field)`), not a special tuple-binding arm. So the original branch is gone; this guards the flat emission that replaced it. New test `multi_outfield_producer_emits_one_binding_per_field` (+ two fixtures): a two-field producer (`hi`:f64, `lo`:i64 — distinct kinds, so the per-field `field_kind` lookup is exercised) re-exported as both composite outputs, nested in a root so it renders through `composite_def_json`. Asserts both fields surface with their own kind (`"outputs":[["hi","f64"],["lo","i64"]]`) and both bindings reference the same producer at distinct ordinals (`0.o0`/`#0`, `0.o1`/`#1`). Test-only; no production code touched (the behaviour was already correct). Verified: cargo test -p aura-engine green (237 in the lib suite); clippy clean. closes #47 |
||
|
|
b8b83cf1c9 |
feat(0091): byte-canonical blueprint emit; record the equality-surface decision
#164 — tighten the C24 "canonical, versioned" blueprint contract on two corners the milestone fieldtest surfaced. Both forks were derivable; resolved with rationale (recorded on #164). Fork 1 — canonical artifact carries no trailing newline. `blueprint_to_json` returns the JSON value with no trailing newline (647 bytes for the SMA-cross fixture); `aura graph build` had framed it with a `println!` (648). The library serializer is the single canonical source — the form content-addressed topology identity (#158) hashes — so the CLI is a transport that must not mutate the bytes: `build_cmd` now `print!`s the canonical bytes verbatim, making the CLI and library emit paths byte-identical. RED-first: the new E2E `graph_build_emit_is_byte_canonical_no_trailing_newline` pinned the trailing newline (FAIL), green after the one-line change. The two cycle-0088 `.out.json` goldens are re-recorded at 647 bytes. The loader stays lenient (a trailing newline on input still parses, Tier-1 robustness; the milestone fieldtest's `mt_4` tolerance check is unaffected). Fork 2 — the canonical JSON is the blueprint equality/identity surface; no second in-memory PartialEq/Debug is added. `Composite`/`BlueprintNode` cannot derive them: `PrimitiveBuilder` (aura-core node.rs) holds a `build: Box<dyn Fn(&[Cell]) -> Box<dyn Node>>` closure, neither comparable nor printable. Equality could only be defined over the serialized data, of which `blueprint_to_json` is already the single source; a separate in-memory equality notion would be a redundant second source of truth and a drift hazard against the form #158 hashes. Recorded the decision (discharges acceptance box 2's "or JSON-string is the intended equality surface" arm) in the C24 ledger; no code added for it. Verified: cargo test --workspace green (51 suites, incl. the new RED->green E2E); cargo clippy --workspace --all-targets -D warnings clean. closes #164 |
||
|
|
8bb21b1c16 |
docs(0088): document op-script grammar; refresh stale cycle-0088 example corpus
#163 — the construction op-script (`aura graph build`) had no durable public
schema doc, and the only worked-example corpus had drifted out of sync with the
current binary, so a consumer copying the visible example was misled.
Grammar reference (durable): a compact op-script grammar — the six verbs
(source/input/add/feed/connect/expose), their keys, the typed-Scalar bind form
({"I64":2}) and the capitalized ScalarKind, the by-identifier / dotted-port
model, and the add-`name` vs expose-`as` distinction — added to the C24 ledger
entry, with a correct worked-example pointer. Surfacing the same grammar in
`aura graph build --help` is left to ride with the CLI-discoverability work
(#159), keeping this change decoupled from the deferred World/project-as-crate
layer.
Corpus refresh: the cycle-0088 fixture corpus replayed against the current
binary failed on two accumulated drifts. (1) `add`'s naming key was renamed
`as` -> `name` (3c4b667; `expose` keeps `as` as a real alias) — the inputs now
use `name` for `add` ops only. (2) The dataflow-cycle bug the 0088 fieldtest
found was fixed (
|
||
|
|
3f75d59598 |
audit(0090): cycle + C24 milestone close — fieldtest green; ledger + rm ephemeral spec/plan
Closes the cycle-0090 (#156) work and delivers the "Topology-as-data: blueprint serialization & loader (C24)" milestone. Cycle-0090 audit (architect drift review, range 7ad7f58..HEAD): drift-clean on substance. Tier-1 forward-tolerance is genuinely serde-default (deny_unknown_fields = 0 in every production struct), pinned at envelope + primitive across both the in-crate and public-seam tests; Tier-2 refusals real and green; no struct/signature/runtime change; the invariant-5 acyclicity lockstep (construction gate <-> bootstrap Kahn sort) undisturbed; invariant-9 resolver stays injected, no registry. The one medium item the architect flagged — the C24 Remaining block carried no deferral signal for #158/#159 — is fixed in this commit's ledger update. Milestone fieldtest (fieldtests/milestone-topology-as-data/, the close-gate evidence): GREEN, 0 behavioural bugs. A downstream consumer ran the full author -> serialize -> load -> construct -> introspect -> reproduce story from the public surface alone — round-trip bit-exact on both authoring surfaces (Rust builder + op-script CLI), build-free introspection, Tier-1/Tier-2 forward-compat by name, fail-fast diagnostics by identifier. Verified the headline claims myself (the two Rust binaries + the CLI build/diff + the fail-fast exits), not just the agent's report. Findings triaged to the forward queue (none blocking): op-script grammar undocumented + a stale cycle-0088 example (#163); canonical trailing-newline divergence + Composite Debug/PartialEq ergonomics (#164); CLI discoverability of build/introspect + no `graph run` (commented on #159); the newline as a content-addressing prerequisite (commented on #158). #158 (content-address topology in the manifest) and #159 (retire the hard-wired harnesses) were moved OUT of this milestone: both are premature until the World generates runs from blueprint-data (commit still fully identifies every hard-wired topology), so they belong to the project-as-crate / World cycle. The C24 ledger Status records the delivery and the deferral. Ephemeral cycle-0090 spec + plan removed per the lifecycle convention. The Gitea milestone container is closed on user ratification (this run tees up the close, does not click it). |
||
|
|
cdd2da6337 |
feat(0090): blueprint format forward-compat — Tier-1 tolerance pinned, two-tier discipline codified (closes #156)
The blueprint data format (C24) extends additively under one codified two-tier discipline: - Tier-1 (additive-optional): a new optional field/section is tolerated by an older reader (serde ignores unknown fields — no deny_unknown_fields), with no format_version bump; by C1 a new optional field defaults to prior behaviour, so an old blueprint reproduces the same graph. - Tier-2 (must-understand: a new node type, edge semantics, or structural-axis kind): bumps format_version, so an old reader refuses cleanly (LoadError::UnsupportedVersion / UnknownNodeType) rather than silently building a different graph. Most of #156 already shipped with #155 (cycle 0087): the envelope, omit-defaults canonical form, and both Tier-2 load-path refusals were present and green. This cycle pins the one previously-unproven property — Tier-1 forward-tolerance of an unknown optional field on the current loader — and codifies the discipline. Changes: - New characterization pin (in-crate, blueprint_serde.rs): unknown_optional_field_is_tolerated_byte_identically — an unknown optional key at doc and primitive level loads byte-identically and runs bit-identically. - Two public-seam E2E pins (blueprint_serde_e2e.rs, added by the E2E phase): unknown_envelope_field_tolerated_through_public_api and unknown_primitive_field_tolerated_through_public_api — split the two distinct tolerance surfaces (envelope vs primitive) across the World/cdylib crate boundary, where a deny_unknown_fields added to one would break forward-compat while leaving the in-crate test green. - LoadError::UnsupportedVersion doc reworded from the deferred "is #156" note to the settled two-tier discipline statement. - C24 ledger (docs/design/INDEX.md): Status codification sentence added; #156 dropped from Remaining (#158/#159 stay). Derived fork (recorded on #156): the per-section required-flag mechanism (PNG critical-vs-ancillary) is NOT built — the version envelope already gives the must-understand refusal at version granularity, no current Tier-2 section validates a finer scheme, and C1 holds either way. The pin is a characterization test (GREEN on first run) — it documents existing serde silent-ignore behaviour, no production code changed. Verified: cargo test -p aura-engine green (all targets, three new tests confirmed by name); cargo clippy --workspace --all-targets -- -D warnings clean. |
||
|
|
ead064df50 |
plan: 0090 blueprint forward-compat
Single-task iteration for #156: append the Tier-1 forward-tolerance characterization test (unknown_optional_field_is_tolerated_byte_identically), reword the LoadError::UnsupportedVersion doc to the settled two-tier discipline, and codify it in the C24 ledger (Status sentence + drop #156 from Remaining). No struct/signature/runtime change. refs #156 |
||
|
|
cb558519b0 |
spec: 0090 blueprint forward-compat two-tier discipline (boss-signed)
Iteration within the C24 topology-as-data milestone (#156). Codifies the blueprint format's additive forward-compatibility under one discipline: Tier-1 (additive-optional) = serde-default silent-ignore, no version bump, C1 default-equals-prior-behaviour; Tier-2 (must-understand: new node type, edge semantics, structural-axis kind) = bump format_version so an old reader refuses cleanly rather than building a different graph. Most of #156 already shipped with #155 (cycle 0087): the format_version envelope, omit-defaults canonical form, and both Tier-2 load-path refusals (LoadError::UnsupportedVersion / UnknownNodeType) are present and green. The genuine remainder is one unproven property — forward-tolerance of an unknown optional field on the current loader — plus codifying the discipline in the LoadError doc and the C24 ledger entry. Not a new mechanism. Derived fork recorded on #156: the per-section required-flag mechanism (PNG critical-vs-ancillary) is NOT built — the version envelope already gives the must-understand refusal at version granularity, there is no current Tier-2 section to validate a finer scheme against, and C1 holds either way. Boss-signed on a grounding-check PASS (all current-behaviour claims ratified by named green tests; the new Tier-1 pin correctly absent — this cycle's deliverable). refs #156 |
||
|
|
3c4b667955 |
feat: construction add names a node via name, mirroring the builder (#157)
The op-script authoring form is meant to be the Rust builder lifted into data —
every op is a 1:1 image of a GraphBuilder method. The one un-builder-ish key was
`add`'s `"as"`: the builder names a node with `.named("fast")`, so the data echo
is `"name"`, not the SQL-ish aliasing word `as`. Renamed the `add` wire key
`as -> name`.
`expose` keeps `as` — there it is a genuine alias (`expose bias.bias as bias`,
mirroring the builder's positional name arg). `source`/`input` keep their terse
verbs: those are plain shortenings of `source_role`/`input_role`, not a foreign
concept the way `as`-for-naming was.
CLI-side only: a one-attribute serde rename on the `OpDoc` Add DTO
(`#[serde(rename = "name")]`), since the engine `Op` is serde-free (the engine
field stays `as_name`, untouched). Updated the op-list test documents to the
`name` key (add ops only; expose's `as` left intact) and added an explicit pin
(`add_names_node_via_name_key`). The cycle-0088 fieldtest corpus
(fieldtests/cycle-0088-construction-op-script/*.json) is left on the old `as`
form as historical cycle-0088 evidence (it is not run by the suite).
Verified: full workspace suite + clippy green; `aura graph build` on a `name`-keyed
doc names the node and emits it in the #155 blueprint.
|
||
|
|
7ad7f58798 |
audit(0089): cycle close — record acyclicity lockstep + C24 ledger refresh; rm ephemeral spec/plan
Cycle-close audit for the /boss run that followed up the cycle-0088 construction fieldtest (#161 cycle-reject + #162 diagnostics-by-identifier). Regression gate: full workspace suite green (0 failed). Architect drift review (range 86841b0..HEAD): drift_found, two items, both documentation-side — the code is correct and green. - What holds: invariant 5 / C9 acyclicity (closes_cycle is a correct eager reachability gate, no false positive on a valid DAG); C24 no-second-validator (blueprint.rs untouched, finish() only translates the gates' returned CompileError); fieldtest corpus handled per convention. - Resolved (fix path, ledger): the C24 Status now enumerates the #161 eager acyclicity gate and captures the LOCKSTEP the architect flagged — closes_cycle (construction-time) and the bootstrap Kahn sort (harness.rs, BootstrapError::Cycle) are now two homes of invariant-5 that must co-evolve when the explicit delay/register node lands; plus a note that the holistic finalize faults now read by-identifier (#162) while still calling the unchanged gates. Ephemeral 0089 spec + plan git-rm'd per the active-cycle artefact lifecycle; the durable rationale is lifted to the ledger above. No code change in this commit. |
||
|
|
f1ab81d1b8 |
feat(0089): construction diagnostics read by-identifier (closes #162)
The cycle-0088 fieldtest found three presentation defects on the construction
op-script surface, all where the surface dropped out of its by-identifier register.
Presentation only — every fault still fires identically (same exit code, same
op/finalize attribution); only the rendered cause changes.
- Item 1 (finalize by-identifier). The holistic finalize fault leaked the raw
index Debug form (`UnconnectedPort { node: 2, slot: 1 }`). `GraphSession::finish()`
now translates the three reachable index-carrying CompileErrors into new
by-identifier OpError variants (UnconnectedPort{node,slot} / RoleKindMismatch{role}
/ UnboundRootRole{role}) using the session's `ids`/`schemas` and the composite's
`input_roles()` — the holistic gate calls (validate_wiring,
check_param_namespace_injective, check_root_roles_bound) stay byte-for-byte
unchanged (C24: no second validator; only the `.map_err` closures changed).
`aura graph build` now prints `finalize: slot sub.rhs is unconnected`.
- Item 2 (bind mismatch prose). `format_op_error`'s BadParam arm matches the
BindOpError variants instead of `{:?}`: `op 1 (add): param fast.length expects I64
but got F64`, matching the connect mismatch register.
- Item 3 (discoverable bind form). `introspect --node` shows the typed-Scalar bind
form: `param length:I64 (bind {"I64": <v>})`.
Item 1 spans both crates atomically (adding OpError variants forces the exhaustive
format_op_error match; the finish() translation flips behavioural test pins in both
crates). Five test touches: the two in-file unit tests, the two E2E twins plan-recon
found that the spec's testing strategy under-enumerated
(construction_e2e.rs, tests/graph_construct.rs), and a new unbound-root-role test;
the implementer added RoleKindMismatch / Ambiguous/UnknownParam coverage too. The
CLI finalize test was tightened (orchestrator) to pin `sub.rhs` and the absence of a
raw `node:` index.
Spec boss-signed on a grounding-check PASS (docs/specs+plans/0089). Verified: full
workspace suite + clippy green; the three messages reproduce exactly against the
built binary on the fieldtest fixtures.
|
||
|
|
c6c181f715 |
plan: 0089 construction diagnostics polish (refs #162)
Two tasks: item 1 (by-identifier finalize) lands atomically across both crates — three OpError variants + finish() translation + the CLI presenter arms + five test touches (two of them E2E twins plan-recon found that the spec's testing strategy under-enumerated: construction_e2e.rs and tests/graph_construct.rs); items 2 & 3 (bind-mismatch prose + discoverable bind form) are additive CLI changes. |
||
|
|
365f112451 |
spec: 0089 construction diagnostics polish (boss-signed) (refs #162)
Three presentation-only defects the cycle-0088 fieldtest found on the
construction op-script surface: the holistic finalize fault leaks raw machine
indices (UnconnectedPort { node: 2, slot: 1 }) where the by-identifier mapping
demonstrably exists; the bind-kind mismatch leaks a Debug struct; the typed-Scalar
bind form is invisible from introspect --node. Boss-signed on a grounding-check
PASS (every load-bearing assumption ratified by a currently-green test or the
defining code).
|
||
|
|
16520429be |
fix(0088): reject dataflow cycles in construction build (closes #161)
The construction op-script service accepted a dataflow cycle and exited 0,
emitting a non-DAG blueprint — violating domain invariant 5 / C9 (the graph is a
DAG; the only feedback path is an explicit delay/state node). The cycle-0088
fieldtest surfaced it (fieldtests/cycle-0088-construction-op-script/FINDINGS.md).
The construction gate set had no acyclicity check: a cyclic interior-edge set
passed both the eager per-op gates and the holistic finish, so replay() returned
Ok. Fix: an eager reachability check at the connect op that closes the cycle —
GraphSession::closes_cycle(from, to) asks "does the `to` node already reach the
`from` node through the edges added so far?" and rejects with a new by-identifier
OpError::WouldCycle { from, to } ("connecting from -> to would close a cycle"),
threaded through the CLI's exhaustive format_op_error. The fault is attributed to
the offending connect op, consistent with the surface's eager per-op diagnostics.
Eager rather than a holistic topological check at finish: a cycle is closed by
one specific connect, and naming that connect is the sharpest by-identifier error.
RED-first: two tests pin the symptom (a 2-cycle a<->b and a self-loop), both red
before the fix and green after; the 19 existing construction tests, the full
workspace suite, and clippy stay green.
Scope is the cycle only. The validity-floor cases the fieldtest also noted (an
unfed declared source role, an output-less graph) are degenerate-but-well-formed,
not invariant violations, and are deferred (refs #161) pending the build->consume
edge (#28) that defines what a valid emitted blueprint must guarantee.
|
||
|
|
985e129129 |
test(fieldtest): cycle 0088 construction op-script — downstream consumer pass
Exercise the construction op-script service (#157) purely as a downstream consumer authoring topology as data: discover-then-author an SMA-crossover bias, author-with-`--unwired`, and 15 error-ergonomics fixtures. Public interface only (the C24 ledger + the CLI's own output); no implementation source read. Confirmed working: discover→author round-trip, the `--unwired` by-identifier authoring aid, eager per-op diagnostics. Surfaced one bug + frictions, all reproduced and filed forward: - #161 (bug): `graph build` accepts a dataflow cycle — DAG invariant 5 unenforced; reproduced directly before filing. - #162: construction error/bind ergonomics (raw-index finalize, Debug-struct bind-mismatch, undiscoverable typed-Scalar bind). - #28: no CLI consumer for the emitted #155 blueprint (round-trip dead-ends). - #159: construction surface undiscoverable from the CLI usage/help. FINDINGS.md carries the full report + the triage table. The two 1.4 MB self-contained viewer HTML dumps from the round-trip check are dropped as bloat (the byte-identical result is recorded in FINDINGS). |
||
|
|
86841b0740 |
audit(0088): cycle close — drift fixes + C24 ledger refresh; close construction service
Architect drift review of cycle 0088 (#157, the introspectable fail-fast construction service: §A shared gate predicates, §B the GraphSession/Op/replay per-op surface + introspection, §C the aura graph build/introspect CLI). What holds (confirmed against the diff): no-second-validator (C24) — edge_kind_check is the single kind-check site called by both validate_wiring and GraphSession::connect, and validate_wiring / check_param_namespace_injective are reused verbatim in finish(); engine domain-free (invariant 9) — construction takes an injected Fn(&str)->Option<PrimitiveBuilder>, no registry, the closed match + std_vocabulary_types live in aura-std; topology-as-data / no DSL (C24/C17/inv 10) — the engine Op is serde-free, the OpDoc wire DTO is CLI-side, ops are static and non-Turing-complete; replay-equals-builder byte-identity (construction_e2e.rs) protects C9/C19/C23. Resolution: - fix (drift-med): the root-role-boundness gate was the one holistic check finish() re-implemented instead of sharing — extracted check_root_roles_bound (blueprint.rs) and called from BOTH compile_with_cells and finish(), so finish() now reuses ALL the holistic gates and the no-second-validator claim is literally true. Behaviour- preserving (same UnboundRootRole variant; unbound_root_role_is_rejected stays green). - fix (drift-med): removed the #![allow(dead_code)] on construction.rs — empirically unnecessary (the pub re-exported surface is reachable; clippy -D warnings green without it) — and its now-inaccurate comment (it claimed removal "when the consumer lands", but the consumer is a separate crate). Also tidied a stale graph_construct.rs module-doc line. - fix (ledger): C24 Status refreshed — #157 moved out of "Remaining" with a cycle-0088 realization note (the construction service: eager/holistic gate split, build-free introspection, emits the #155 blueprint); #159 reframed as paired with #157. - carry-on (debt-med, forward-noted): the std_vocabulary roster is now a third hand-kept copy (match + test list + std_vocabulary_types) and introspect --vocabulary escalates #160's gap to user-facing — noted on #160; still fails safe. - carry-on (scaling-low, forward-noted): the engine Op -> wire OpDoc direction is unguarded for future Op variants — noted on #156 (the format-extension track). No architect-sweep / regression script is declared in the project facts, so the suite is the gate: cargo test --workspace 51 suites green; clippy --all-targets -D warnings clean. Cycle drift-clean. Ephemeral cycle artifacts removed (lifecycle convention): docs/specs/0088-*, docs/plans/0088-*. closes #157 |
||
|
|
25e452aaf7 |
feat(0088): §C construction op-script CLI — aura graph build/introspect
Iteration-2 §C of the construction service (#157), the CLI shell over the iteration-1 engine core ( |