c939673aa70f72ecdfa433e426a0b2e562ecae1e
212 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c939673aa7 |
audit: cycle 0037 — drift-clean (bound params in the graph model + viewer)
Close-audit for cycle 0037 (commit range d069484..47e0e60). Architect drift review
against the design ledger + CLAUDE.md domain invariants: no contract drift, no debt.
No regression scripts are configured (the architect is the gate); build + test +
clippy verified green independently by the orchestrator.
cycle 0037 (clean):
- C23 preserved: bind still resolves the slot name to a fixed position and re-splices
the value in the build closure; BoundParam/"bound" is recorded purely beside that
capture, a render/debug symbol dropped at lowering — bootstrap and the run loop
never read bound_params() (only the model serializer does). The compilat stays
index-wired. A genuine twin of the cycle-0035 instance-name thread.
- C14 preserved: the inline no-bind model_golden (sample_root) is byte-unchanged;
the "bound" field is conditional (mirrors "name"), so unbound nodes are
byte-identical, and only the sample fixture's blend node gained the field.
scalar_str is total over all four Scalar variants and deterministic (f64 via {:?}
keeps a decimal point so a bound f64 never reads as an i64).
- C12/C19 preserved: the tunable surface is untouched — bind still removes the slot
from schema.params; the eight-param sweep pins stay byte-identical with weights[2]
absent. The render annotation reads from a separate bound vec, not param_space().
Test coverage: the original-position unit test (trailing + reverse-chained), the
prim_record conditional-field unit test (i64 + f64), and a headless render guard
pinning slot-order faithfulness for both a trailing and a middle bind (the middle
case catches an append-only regression).
Resolution: carry-on. Next iteration: pick the next backlog item.
|
||
|
|
47e0e605e1 |
feat(aura): surface bind-bound params in the graph model + viewer signature
A node built with `.bind(slot, value)` fixes a declared param to a structural
constant: the slot correctly leaves the tunable surface (param_space / sweep axes),
but it also vanished from the RENDERED signature, because prim_record emitted only
schema.params. The cycle-0036 `blend` (a LinComb(3) with weights[2] bound to 0.5)
rendered `LinComb[weights[0], weights[1]]` — a 2-arity signature over a 3-port box,
with the fixed 0.5 invisible. The signature misrepresented the node.
bind now ANNOTATES instead of erasing. All slots are shown; the bound one renders
`name=value`, dimmed. The blend renders:
blend: LinComb[weights[0], weights[1], weights[2]=0.5]
Structurally a twin of cycle 0035's instance-name thread (commit
|
||
|
|
4227073fce |
plan: 0037 bound-param-in-graph-model
Six bite-sized tasks for spec 0037: aura-core records a BoundParam (original slot position) in bind + a bound_params() accessor; aura-engine emits a conditional "bound" field via scalar_str; graph-viewer.js merges free+bound params into slot order and renders the bound slot dimmed as name=value; re-capture the sample-model fixture; a new headless render guard (trailing + middle bind); full-workspace gate. refs #63 |
||
|
|
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 |
||
|
|
d069484559 |
audit: cycle 0036 — refresh stale sample cross-refs; drift-clean otherwise
Close-audit for cycle 0036 (commit range 560c2d0..94af4c7). Architect drift review against the design ledger + CLAUDE.md: no contract drift. No regression scripts are configured (the architect is the gate); build + test + clippy verified green. cycle 0036 tidy (clean): - C8/C9/C10 preserved: the enriched sample is multiply-nested composites over shipped primitives — multi-output is the macd composite re-exporting columns (not a >1-record primitive); brokers stay downstream nodes. - C12/C19 preserved: the eight sweep axes are a bijection with the injective, path-qualified param_space under nesting; the bound blend.weights[2] is correctly absent from both the axes and the surface. The re-captured render golden, the two in-crate pins, and the cli_run E2E pin all carry the identical eight-tuple in lockstep. - C14 preserved: the re-captured sample-model.json is the deterministic model of the enriched sample; the engine's own minimal model_golden (sample_root) is independent and byte-unchanged. - C23 preserved: bind resolves the param name to a fixed position; the fixed blend weight is a structural constant (deform-not-tune), dropped from the tuning surface, and the compilat is unchanged. Two stale cross-references found and refreshed (doc debt, no behaviour change): - crates/aura-engine/src/graph_model.rs: sample_root's "Mirrors build_sample() (...:161-190)" was doubly stale — build_sample moved and the CLI sample is now the richer signals graph; re-grounded to state sample_root is a deliberately minimal, independent serializer fixture. - crates/aura-cli/src/main.rs: sample_blueprint_with_sinks' "single source of the sample topology" + "SMA lengths + exposure scale" overclaimed post-enrichment; re-grounded to the root harness whose signal is the nested signals composite, with the eight free params. |
||
|
|
94af4c788c |
feat(aura-cli): enrich the graph sample into a trend+momentum blend showcase
The built-in sample that `aura graph` renders and `aura sweep` runs is now a
"trend + momentum, weighted blend" strategy that exercises four authoring/viewer
capabilities the single-level sample left dark:
- multiply-nested composites: root -> signals -> {trend = sma_cross, momentum = macd};
- a multi-param node inside a composite: blend = LinComb(3) (weights[0]/[1] shown);
- a multi-output node: momentum (the macd composite) re-exports macd/signal/histogram,
two of which the blend consumes;
- bind(): blend.weights[2] is fixed as a structural constant, so it drops out of the
sweepable param surface (and renders absent).
The new `signals` composite reuses the existing sma_cross/macd builders unchanged; the
sweep re-paths its axes to the eight free params (still a 4-point grid) and runs an
18-tick warm-up stream (showcase_prices) so the MACD EMA-of-EMA chain and the all-Any
LinComb join warm up. The render fixture (sample-model.json) is re-captured, and a new
headless guard (viewer_nested_depth) pins that the viewer renders the nesting to depth 2
with valid Graphviz ids. Pure authoring over already-shipped primitives — no engine or
graph-viewer.js change.
The flat run_sample, run_macd/macd(), the inline model_golden test, and the viewer are
untouched. A third sweep-param pin in tests/cli_run.rs (the aura sweep E2E) was re-pathed
in lockstep with its in-crate twin.
closes #62
|
||
|
|
30c1ad5e2e |
plan: 0036 sample-showcase-blueprint
Five tasks for the sample-showcase cycle: (1) the enriched blueprint source — LinComb import, the `signals` composite (trend = sma_cross, momentum = macd, blend = LinComb(3) with weights[2] bound), the node-0 swap in sample_blueprint_with_sinks, the re-pathed sweep_family axes + a showcase_prices warm-up stream; (2) re-path the two in-crate tests (bootstrap-runs-drains, sweep odometer); (3) re-capture the sample-model.json render fixture; (4) a new headless depth-2 nesting guard (viewer_nested_depth.mjs + .rs); (5) the workspace build/test/clippy gate. refs #62 |
||
|
|
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 |
||
|
|
560c2d0824 |
audit: cycle 0035 — drift-clean (node instance name in the graph model)
Close-audit for cycle 0035 (commit range 20000dd..0ae8320). Architect drift
review against the design ledger + CLAUDE.md: clean. No regression scripts are
configured (the architect is the gate); build + test + clippy verified green.
cycle 0035 tidy (clean):
- C23 preserved: the instance name is resolved to Option at authoring
(instance_name()), emitted only on the render/model surface, and dropped at
lowering — the compilat wiring is byte-unchanged.
- C14 preserved: model bytes stay deterministic; both golden twins (inline
model_golden + sample-model.json) moved in lockstep to the same two "name"
additions.
- C12/C19 untouched: the cycle adds no param-space surface; the param-ganging
alternative was deflected to idea #61 (not via name collision — param_space
is injective by design).
- Engine<->viewer lockstep intact: the conditional "name" producer (prim_record)
and its three consumers (adaptNodes, genDot leaf-emit, cellLabel) moved
together, protected by a prim_record unit test + a headless render guard.
The blueprint-viewer `SMA[length]` (
|
||
|
|
0ae8320d14 |
feat(aura): surface explicit node instance name in the graph viewer
A leaf primitive built with `.named("fast")` now renders its `aura graph` viewer
box head as `fast: SMA[length]` — the instance name as a `:` declaration prefix.
An unnamed leaf keeps the bare `SMA[length]`.
Engine half: a new raw `PrimitiveBuilder::instance_name() -> Option<&str>` (the
explicit name, default not resolved) feeds a conditional leading `"name"` field
in prim_record, present only for an explicitly-named node; both golden twins
(inline model_golden + sample-model.json) re-captured. Viewer half: adaptNodes
carries `name`, the genDot leaf-emit forwards it, and cellLabel prepends the
`name: ` prefix when present (composites stay bare). `named()`'s non-empty
debug_assert is unchanged, its doc re-grounded to the now load-bearing Some/None
invariant (knob-address segment + prefix switch).
The name is a render/model-only debug symbol — dropped at lowering (C23), and the
model stays deterministic (C14). Parameter ganging (one knob, several nodes) was
considered and spun off as an explicit composite-shared-param idea (#61), not via
name collision (param_space is injective, C12/C19).
closes #58
|
||
|
|
77ddbeb237 |
plan: 0035 node-name-in-graph-model
Six tasks for the node-name-in-graph-model cycle: (1) aura-core instance_name() accessor + re-grounded named() doc; (2) aura-engine prim_record conditional "name" field + unit test; (3) re-capture both golden twins (inline model_golden + sample-model.json); (4) viewer render guard (RED, headless node guard); (5) viewer adaptNodes/genDot/cellLabel name prefix (GREEN); (6) workspace build/test/clippy gate. refs #58 |
||
|
|
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 |
||
|
|
a051571d79 |
feat(aura-cli): render leaf param signature as TYPE[...] in the graph viewer
The viewer box head built the param signature in round parens — SMA(length) — which reads as a function call. Switch the brackets to square — SMA[length] — so the head reads as parametrisation, not invocation. cellLabel render-only change in graph-viewer.js; the JSON model surface and both goldens are untouched. |
||
|
|
3c698d4200 |
chore: move skills-plugin profile into CLAUDE.md project facts
The skills plugin dropped dev-cycle-profile.yml. Migrate this project's facts into CLAUDE.md under '## Skills plugin: project facts' and remove the profile file. |
||
|
|
20000ddeb3 |
audit: cycle 0034 (#55) — record structural-constant bind in the ledger; drift-clean otherwise
Architect drift review over 52e0214..HEAD. All hard invariants hold; the only drift was a ledger-prose gap — the new .bind() structural-constant affordance had no contract entry, so the code offered a capability the contracts did not name. Contracts (all hold): - C23: bind resolves a param name -> position at authoring time and stores the index; the compilat stays wired by raw index, the name never reaches it. The by-name authoring address space (0032 amendment) is exactly where bind sits. - C19/C8: the shrink is schema.params.remove(pos) on the single source of truth that params()/schema()/collect_params/lower_items all read, so no desync is structurally possible; param_space() injectivity (0032) is untouched (bind only removes entries). The param-declared-once posture holds. - C9/C10: a pure builder-level value op — no by-name runtime node lookup, no registry, no DSL-style dynamic resolution. - C16: zero Cargo.toml / lockfile changes. - Construction layer (collect_params/lower_items/param_space/compile_with_params) byte-unchanged — verified independently; the blueprint.rs diff is a test-only hunk at 1886, the sole production edit is bind in aura-core/src/node.rs. Regression: none configured (profile regression: []) — no-op; architect is the gate. Resolution -- fix (1 ledger note, added inline this commit): - docs/design/INDEX.md C19 — added a cycle-0034 realization note recording PrimitiveBuilder::bind as the structural-constant path (knob REMOVED from param_space), distinct from the cycle-0016 value-pin (knob stays, fixed in the injected vector); the #55 deform-vs-tune discriminator; C23 unaffected; export of a named frozen strategy deferred to #60. Cycle 0034 drift-clean after this note. Not a milestone close (no milestone fieldtest run). Gates: doc-only edit, compile-inert; cargo build/clippy/test --workspace verified green this session. |
||
|
|
7d587d0c4e |
feat(aura-core): PrimitiveBuilder::bind — fix a node param as a structural constant
Add `PrimitiveBuilder::bind(slot, value)`: bind a declared param to a structural constant, removed from param_space entirely rather than pinned in the injected vector. This closes the gap #55 names — param-bearing nodes (Sma/Exposure/LinComb) had no constant path, only the no-sweep-mode SimBroker precedent. The motivating case is "SMA2-entry": the 2 is bound to the two-candle construction, so sweeping it would enumerate deformed strategies as valid family members. Approach (Option B, builder-level — the originally-planned direction): bind shrinks the builder's declared param surface (schema.params) and wraps its build closure to re-splice the captured constant at its original positional slot. The construction layer (collect_params / lower_items / param_space / compile_with_params) is byte-unchanged: both dock sites already key off builder.params(), so the shrink propagates for free. Addressing is by param name (the by-name authoring address space, C23/0032); the compilat stays wired by raw index — names never reach it. bind enforces "exactly one open param matches" itself (collect-all-then-reject, panic on zero / on duplicate), mirroring the resolve binder's posture, because a node's own schema.params is not covered by check_param_namespace_injective. Chained binds reconstruct the correct positional vector because each layer computes its slot index relative to the param list it sees (no global position table). Considered and rejected: a per-node builder_const(N) constructor (explodes combinatorially over which subset is constant, not dynamic); a ParamBinding overlay struct on Composite (would need new bookkeeping the builder-level form avoids). Verified: cargo build/clippy/test --workspace all green; new tests cover slot removal, positional reconstruction (chained + partial, via eval), the three panic paths, and the composite param_space projection; blueprint.rs construction layer confirmed byte-unchanged. Deferred (out of scope, follow-up issue): exporting a named frozen strategy as a blueprint-as-values collection — the issue's third fork, needs its own brainstorm. closes #55 |
||
|
|
3413194809 |
plan: 0034 blueprint-constant-bind
Task decomposition for PrimitiveBuilder::bind (spec 0034): T1 the method + aura-core unit tests (Probe node drives positional reconstruction RED→GREEN; three should_panic), T2 node-vehicle coverage (Sma/LinComb via eval in aura-std), T3 Composite param_space coverage + construction-layer zero-change guard + workspace gate. Crate-topology aware: aura-core unit tests use a local Probe, not Sma/LinComb (no upward dep). refs #55 |
||
|
|
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 |
||
|
|
52e0214adb |
audit: cycle 0033 (#54) — refresh stale JSON-writer cross-refs after to_json→serde
Architect drift review over 065cf1d..HEAD. Code + contracts hold; the only drift was documentation cross-references made stale by retiring RunReport's hand-rolled to_json. Contracts (all hold): - C14: to_json still emits canonical, flat, deterministic JSON — now serde-produced. model_to_json (the graph-model JSON) stays hand-rolled and was correctly untouched. - C16: serde_json dev-dep → normal-dep in aura-engine is squarely sanctioned by the amended per-case policy (INDEX.md:576-583) — a vetted standard crate used where it does the job (incl. the frozen bot), removing a hand-rolled writer. No gap. - C1: report types have no HashMap; serde is declaration-order/deterministic; the r1.to_json()==r2.to_json() asserts and the new to_json_equals_serde_disk_shape pin stay green. Regression: none configured (profile regression: []) — no-op; architect is the gate. Resolution — fix (3 prose cross-refs, fixed inline this commit): - graph_model.rs:5 (module doc) — claimed the model JSON is in the "RunReport::to_json house style (no serde)"; to_json is serde now. Reframed: the model JSON is hand-rolled (no serde), and is the engine's last hand-rolled JSON writer since 0033. - graph_model.rs:30 (json_str doc) — cross-referenced RunReport::json_str, deleted this cycle. Reworded to describe the escape set directly (graph_model's own json_str stays). - docs/design/INDEX.md:729 — "golden-tested like RunReport::to_json" framed the hand-rolled model JSON by a now-serde to_json. Reframed to name model_to_json as the surviving hand-rolled writer. Cycle 0033 drift-clean after this tidy. Not a milestone close (the param-sweep milestone fieldtest is a separate deliberate act). Gates: clippy --workspace --all-targets -D warnings clean (doc-only edits, compile-inert). refs #54 |
||
|
|
f1d0bf00ec |
fix(aura-engine): unify RunReport stdout JSON with the on-disk serde shape
RunReport had two divergent JSON encoders: the registry serialized to disk via serde_json (params as an array-of-pairs, floats as 2.0), while stdout rendered through a hand-rolled to_json (params as an object, whole floats as 2). The same record was a structurally different document on disk than on the wire, and no test pinned their relationship — most visibly, `aura runs list` read a record from disk via serde and reprinted it in a different shape. Retire the hand-rolled writer: RunReport::to_json now delegates to serde_json::to_string(self), so stdout is byte-identical to the runs.jsonl line for the same record. The hand-rolled json_str helper is deleted (the graph_model.rs json_str is a separate symbol, untouched — the graph-model JSON is out of scope, #58/#28). A new pin test, to_json_equals_serde_disk_shape, asserts to_json() == serde_json::to_string(&report) so the two encoders can never silently diverge again. Observable change: stdout `params` is now an array-of-pairs and finite f64 fields carry a fractional part (2 -> 2.0); the nested envelope, field order, window [from,to], and integer-valued seed/exposure_sign_flips are unchanged. The on-disk shape does NOT change — only stdout moves to match disk. serde_json is promoted from a dev-dependency to a normal dependency of aura-engine (to_json is non-test code). Under the amended C16 per-case policy this passes: serde_json is a vetted standard crate already in the workspace (registry, ingest), deterministic (C1-safe, already relied on for the disk path), and is exactly "the vetted standard crate doing what the code would otherwise hand-roll" — it removes the last hand-rolled RunReport JSON writer. Goldens flipped to the serde shape: the engine canonical-form golden, the aura-cli sweep golden (cli_run.rs), and the aura-cli odometer-order sweep golden (main.rs) — the last was not in the plan's inventory; the cargo test --workspace gate surfaced it and it took the identical transformation. Structural CLI asserts and the r1.to_json()==r2.to_json() determinism asserts are shape-agnostic and stay green. Spec docs/specs/0033, plan docs/plans/0033, boss-signed (unanimous 5-lens panel). Two stale prose cross-refs (graph_model.rs:30, docs/design/INDEX.md:729) deferred to cycle-close audit. Gates: cargo build/test --workspace green, clippy --all-targets -D warnings clean. closes #54 |
||
|
|
e2056b6436 |
plan: 0033 unify-runreport-json-serde
Two-task RED→GREEN plan for spec 0033. Task 1 (RED, test-only): flip the engine canonical-form golden + the CLI sweep golden to the serde array-of-pairs/.0-float shape, add the to_json_equals_serde_disk_shape pin test — all fail against the current hand-rolled to_json. Task 2 (GREEN, atomic): promote serde_json dev-dep -> normal dep, swap to_json's body to serde_json::to_string, delete the now-unused json_str helper, refresh the method + module-header doc comments — one build+clippy gate flips every Task-1 assertion green. graph_model.rs:30 + INDEX.md:729 stale cross-refs deferred to cycle-close audit. refs #54 |
||
|
|
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 |
||
|
|
065cf1df1e |
audit: cycle 0032 (#59) — drift-clean; param_space() injectivity is a compile invariant
Architect drift review over 11dfff8..HEAD: clean, no drift, no debt. - C9/C12/C19/C23 ledger prose matches the landed code exactly. The C9 refinement names the retired fan-in machinery (signature_of, leaf_has_param, check_fan_in_distinguishability, check_composite_fan_in) as retired, names the two intentionally-dropped render-identity cases (asymmetric param/paramless, role-vs-leg) as deliberate drops, and points at DuplicateParamPath/.named(). - C23 honoured: the check reads only the param_space() boundary name projection; the compilat stays index-wired, no name became load-bearing. - C1 preserved: param_space() stays infallible; the gate is pure pre-build structural validation; injective blueprints compile bit-identically. - Drift hazards all clear: no live reference to the retired machinery (only frozen historical specs/plans/fieldtests + the cycle's own test-doc comments); the stale "CLI render" signature_of doc line was removed with the function; both resolve/resolve_axes callers check injectivity before resolving so the unreachable! arms are genuinely dead; no live contract references the removed IndistinguishableFanIn / AmbiguousKnob variants; engine core untouched. Regression: none configured (profile regression: []) — architect is the gate. Resolution: carry-on (drift-clean). Gates re-run by the orchestrator: cargo build/test --workspace green (0 failed), clippy --all-targets -D warnings clean. Drift-clean is not a milestone close (the milestone fieldtest gate is separate). |
||
|
|
1b7e4ad169 |
feat(aura-engine): param_space() injectivity as a compile invariant
A non-injective param_space() — two slots under one path-qualified name — is the by-name knob address space (C12/C19) being broken: no binding can select one slot without the other. This cycle makes it a structural compile error. One check, check_param_namespace_injective over the param_space() names, replaces the whole fan-in machinery (signature_of, leaf_has_param, check_fan_in_distinguishability, check_composite_fan_in) and runs from compile_with_params AND from both binders before resolve/resolve_axes — so the canonical by-name author sees the structural cure (CompileError::DuplicateParamPath, carrying the path and pointing at .named(...)) instead of the AmbiguousKnob symptom (#59). With an injective space guaranteed before resolve, no bound name can multi-match, so BindError::AmbiguousKnob is dead and removed (both resolver arms -> unreachable!). param_space() stays infallible; the invariant lives in the compile step (C23). The check is strictly the by-name-addressability property. The old fan-in predicate (equal signature AND >=1 param) also rejected two collisions that are NOT param_space duplicates — an asymmetric param/paramless collision and a role-vs-leg collision — which guarded render identity, dead since the renderer was retired in 0026. Both are intentionally dropped; a future node-/wiring-name check, if wanted, is decoupled from injectivity. The new invariant correctly rejected a pre-existing fixture (multi_output_composite's two unnamed Sma legs); cured in place with the .named(fast/slow) the invariant mandates. A new E2E test drives the cured cross through the by-name binder end-to-end (resolve + bootstrap + run to a populated exposure trace). C9/C12/C19/C23 ledger notes amended. Verified: cargo build/test --workspace green (0 failed), clippy --workspace --all-targets -D warnings clean. closes #59 |
||
|
|
770cf7177d |
plan: 0032 param-namespace-injectivity
Three tasks for the single-iteration cycle: - Task 1 (RED): add CompileError::DuplicateParamPath + the three new tests (non-fan-in duplicate, asymmetric nested collision, by-name #59 regression), verified failing against current code. - Task 2 (GREEN): add check_param_namespace_injective, wire it into compile_with_params + both binders (before resolve), remove the fan-in machinery (signature_of/leaf_has_param/the two check fns) + IndistinguishableFanIn + AmbiguousKnob (both resolver arms -> unreachable!) + the lib.rs re-export, migrate tests 1/2, remove tests 7/8 + macd fixture. One cargo build --workspace gate over the whole enum-coupled change. - Task 3: amend the C9/C12/C19/C23 ledger notes. refs #59 |
||
|
|
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
|
||
|
|
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 |
||
|
|
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. |
||
|
|
0e411f1796 |
audit: cycle 0031 (#56) — drift fixed (stale C9 render prose); node-instance naming
Architect drift review of cycle 0031 (node-instance naming; spec |
||
|
|
ffed8cc612 |
feat(aura-core,aura-engine,aura-cli): node-instance naming retires ParamAlias
Every blueprint node now carries a name. A primitive builder gains an optional
instance name (Sma::builder().named("fast")); when omitted it defaults to the
node's type label, ASCII-lowercased verbatim ("SMA"->"sma", "SimBroker"->
"simbroker"). param_space() is now uniformly <node>.<param> at every level
including the root (sma_cross.fast.length, exposure.scale). Fan-in
distinguishability (C9) and signature_of re-key onto the node name: two same-type
siblings both defaulting to "sma" collide and IndistinguishableFanIn fires, so
one act -- naming the legs "fast"/"slow" -- fixes both the naming collision and
the fan-in check. The index-addressed ParamAlias overlay, Composite.params, the
Composite::new 5th argument, aliases_on, and check_alias_indices are removed
(Role.name and OutField.name untouched). Ledger C9 and C23 amended.
This is why the change is correct, not merely convenient: blueprints are
value-empty (C11), so the thing that would otherwise distinguish two SMAs --
their length -- is not present at construction; identity must come from a name,
not a deferred value. Closes the #56 friction surfaced by the param-space &
sweep milestone fieldtest (a freshly-authored 2-SMA cross was unbindable and
would not bootstrap without two hand-counted ParamAlias overlays).
Two plan defects were corrected during implementation and verified:
- the three new fan-in/path tests authored sma_cross at root level, which would
qualify to "fast.length" (root prefix is empty) and is not the nested case the
fan-in check inspects; nesting sma_cross under a root (a shared
sma_cross_under_root helper) restores the asserted "sma_cross.fast.length" and
IndistinguishableFanIn{node:2};
- three cycle-0030 named-binder tests bound the real harness by the old names
(sma_cross.fast / scale) and were migrated to the new path strings, surfaced by
the workspace test gate.
Verified by the orchestrator: cargo build/test --workspace green (198 tests,
0 red), clippy --all-targets -D warnings clean. model_to_json emits the type
label + factory param names (node-name-independent), so sample-model.json and the
graph_model golden are byte-identical (untouched). NodeSchema gained a Default
derive (the builder default-name test constructs an empty schema). fieldtests/
are frozen non-workspace records, not migrated.
closes #56
|
||
|
|
d8900900b5 |
plan: 0031 node-instance naming
Implementation plan for spec 0031 (#56). Three tasks: 1. aura-core PrimitiveBuilder instance-name mechanism (.named()/node_name(), default = lowercased type label) — additive. 2. The engine mechanism + the full compile-coupled migration, as one task by necessity: collect_params node segment, signature_of + fan-in re-keyed onto the node name, ParamAlias / Composite.params / the Composite::new 5th arg / aliases_on / check_alias_indices removed, every in-workspace Composite::new caller and ParamAlias reference migrated, the mirror / top_level (inverted) / signature / CLI-golden assertions updated — all under one cargo build/test --workspace gate that enumerates migration completeness. 3. Ledger C9 + C23 amendments. plan-recon (DONE_WITH_CONCERNS) surfaced two sites the brief missed, both now in the plan: the sweep-golden twin crates/aura-cli/tests/cli_run.rs:214 (lockstep with main.rs), and the model_to_json goldens — resolved as OUT of scope (the JSON model emits the type label + factory param names, both node-name-independent, so sample-model.json and the graph_model.rs golden stay byte-identical; the fixtures migrate structurally only). signature_of stays -> String, re-keyed onto the node name. fieldtests/ are frozen non-workspace records, out of scope. refs #56 |
||
|
|
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 |
||
|
|
03285d1b47 |
fieldtest: milestone the-world param-space & sweep — green, delivers promise
Milestone-scope fieldtest (closing functional gate) for "The World —
parameter-space & sweep" (issues #30-#36). Three curated end-to-end scenarios
derived top-down from the milestone promise, driven from the public interface
only (ledger + specs + rustdoc; no crates/*/src read), built and run from HEAD
via a downstream consumer crate.
Delivery verdict: GREEN. The milestone delivers its promise.
- mw_1 single run bound by name: .with("sma_cross.fast", 2).bootstrap() binds
and runs (12 equity rows); diagnostics precise and knob-named (UnknownKnob /
AmbiguousKnob / KindMismatch).
- mw_2 named-axis sweep + compare: .axis(..).sweep(run) yields a 4-member,
ordered (odometer), disjoint, non-constant SweepFamily; ranking from public
fields. CLI (aura sweep / aura runs rank) delivers the same with no Rust.
- mw_3 structural-constant negative space (deliberate): SMA2-entry's
definitional `2` has no honest expression today -> grounds #55. Out of
milestone scope; not a blocker.
Findings: 0 bugs, 2 working, 3 friction, 1 spec_gap. Verified independently
(re-ran all three from HEAD; confirmed a plain SMA-cross emits sma_cross.length
twice and the shipped CLI sample carries the fast/slow ParamAlias overlays).
Friction (routed to the forward queue, none blocks the gate):
- the promised sma_cross.fast/.slow names are not free — they require
author-added ParamAlias overlays, doubly mandatory because the canonical
2-SMA cross also will not bootstrap without them (IndistinguishableFanIn);
- the sweep closure drops back to positional bind + manual name re-zip.
spec_gap -> #55: the wished-for structural-constant consumer code is recorded
in mw_3 as #55's acceptance evidence.
This commit is the milestone fieldtest itself; it closes no issue. Closing the
tracker milestone stays a deliberate manual act on this green gate.
|
||
|
|
acaa6b0df8 |
audit: cycle 0030 (#35) — drift-clean; named param binding
cycle 0030 tidy (clean). The architect drift review against docs/design/INDEX.md
and CLAUDE.md found the cycle drift-clean: every load-bearing contract the named
binding layer touches holds.
- C1 / C12 / C19 / C23: a pure authoring layer — sweep.rs, the lowering path,
collect_params, and param_space() are unchanged; with/axis/Binder/SweepBinder/
resolve/resolve_axes all terminate into the existing bootstrap_with_params /
GridSpace::new / sweep. No new state, no concurrency, no path into the run loop.
Equivalence pinned by named_binder_runs_bit_identical_to_positional and
named_axes_grid_parity_with_positional. The match key is the exact param_space()
name (p.name == name), read-only.
- C7: pure-equality kind check (v.kind() != p.kind), no coercion.
- C16 / C10: std-only (the
|
||
|
|
937257e368 |
feat(aura-engine,aura-cli): named param binding — sweep axes .axis()/.sweep() (0030 iter 2)
Bind a sweep's axes by name instead of by a positional GridSpace:
bp.axis("sma_cross.fast", [2, 3]).axis("sma_cross.slow", [4, 5]).axis("scale", [0.5]).sweep(run)
The sweep half of spec 0030, completing the named-binding feature (#35). A pure
authoring layer over the existing GridSpace::new / sweep primitives; engine core
untouched (C1/C12/C19/C23).
This iteration:
- resolve_axes(): shares resolve's name->slot mapping (iter 1), adds the EmptyAxis
check (the variant defined in iter 1 is now first constructed here) and a
per-element axis kind-check (every element of each axis, first offending element
in axis order). Its validation is a strict superset of GridSpace::new's
(arity / non-empty / per-element kind), so SweepBinder::sweep's downstream
GridSpace::new(...).expect(...) is infallible by construction — it can never
panic on author input.
- Composite::axis -> SweepBinder -> SweepBinder::sweep (Result<SweepFamily, BindError>).
- The CLI sample sweep converted to the named form (behaviour-preserving).
Plan correction (verified by the orchestrator): the iteration plan said only the
GridSpace import was orphaned by the CLI conversion, but replacing the free
sweep(&grid, ..) call with the .sweep(..) method also orphans the free `sweep`
import — both were dropped from aura-cli to satisfy clippy -D warnings. The Scalar
import (from aura_core) stays used and was untouched.
Verified (run by the orchestrator): 6 new aura-engine tests green —
resolve_axes round-trip, EmptyAxis, MissingKnob, the per-element KindMismatch on a
mixed axis (the superset/no-panic guarantee), a builder no-panic wrong-kind test,
and a GridSpace .points() parity test; the two sweep JSON goldens
(sweep_report_renders_four_points_in_odometer_order + the cli_run integration
golden) green unchanged (name/value/order-preserving conversion); full
`cargo test --workspace` green and `cargo clippy --workspace --all-targets
-- -D warnings` clean.
refs #35
|
||
|
|
abd76c9756 |
plan: 0030 named param binding — sweep axes (iteration 2)
Iteration-2 plan for spec 0030 (the sweep side): two tasks. - Task 1 (aura-engine): resolve_axes (shares resolve's name->slot mapping; adds the EmptyAxis check and a per-element axis kind-check, a superset of GridSpace::new so the downstream .expect() is infallible), Composite::axis / SweepBinder / SweepBinder::sweep, the SweepBinder re-export, and the engine tests (round-trip, EmptyAxis, MissingKnob, per-element KindMismatch on a mixed axis, a builder no-panic wrong-kind test, and a GridSpace .points() parity test). RED-first via a todo!() resolve_axes stub. EmptyAxis is now constructed (was defined in iter 1). - Task 2 (aura-cli): convert sweep_family() grid construction to .axis(...).sweep(...) (keeping the per-point closure and the local `space` it reads for manifest params); drop the now-orphaned GridSpace import (the clippy -D warnings trap this iteration). The sweep JSON param goldens (main.rs + tests/cli_run.rs) are name/value/order- preserving and stay green unchanged. Engine core untouched (C1/C12/C19/C23). refs #35 |
||
|
|
64b19ab3d5 |
feat(aura-engine,aura-cli): named param binding — single-run .with()/.bootstrap() (0030 iter 1)
Bind a blueprint's open knobs by name for a single run instead of by a positional,
Scalar-wrapped Vec in param_space() order:
bp.with("sma_cross.fast", 2).with("sma_cross.slow", 4).with("scale", 0.5).bootstrap()
A pure authoring layer over the existing Composite::param_space / bootstrap_with_params
primitives; the engine run loop and the construction core are untouched (C1/C12/C19/C23
preserved). Raw literals lower via Into<Scalar> (the literal fixes the variant: 2->I64,
0.5->F64, pinned by
|
||
|
|
7cd990f789 |
plan: 0030 named param binding — single-run (iteration 1)
Iteration-1 plan for spec 0030 (the single-run side): two tasks. - Task 1 (aura-engine): BindError vocabulary, the shared `resolve` core (two-phase total error order), Composite::with / Binder / Binder::bootstrap, the lib.rs re-export, and the engine tests (resolve round-trip, one per single-run BindError variant, two precedence tests, the C1 named≡positional equivalence test). RED-first via a todo!() resolve stub, GREEN on impl. - Task 2 (aura-cli): convert the sample single-run test to .with(...).bootstrap() using the exact param_space() names (sma_cross.fast/sma_cross.slow/scale). The full BindError enum (incl. EmptyAxis) is defined now and re-exported, so the unconstructed sweep-only variant is reachable API (not dead_code) — EmptyAxis is constructed in iteration 2. Engine core untouched (C1/C12/C19/C23). refs #35 |
||
|
|
8fae58fdc4 |
spec: 0030 named param binding (boss-signed)
Bind a blueprint's open knobs by name via a fluent builder instead of a
positional, Scalar-wrapped Vec in param_space() order: a single run as
bp.with("sma_cross.fast", 2).with("scale", 0.5).bootstrap(), a sweep as
bp.axis("sma_cross.fast", [2,3]).axis("scale", [0.5]).sweep(run). A pure
authoring layer over the existing bootstrap_with_params / GridSpace / sweep
primitives; the engine core is untouched (C1/C7/C12/C19/C23 preserved).
Ratified design (brainstorm -> specify):
- Fluent builder (.with()/.axis()), not a macro — C10 builder-API idiom.
- Raw literals via Into<Scalar>; the literal fixes the variant (2->I64,
0.5->F64); kind-check is pure equality, no coercion.
- Match key = the EXACT param_space() name (user's Option 1): path-qualified
for composite-interior knobs (sma_cross.fast), bare for root-level knobs
(scale). Short bare names are an authoring choice (promote to a root leaf),
not a job of this layer; no engine change, no unqualified matching.
- .with() not .bind() — bind is reserved for #55's structural-constant overlay.
- Total error order (a-f) over a single BindError vocabulary: Phase 1 validates
bindings (UnknownKnob/AmbiguousKnob/EmptyAxis/DuplicateBinding), Phase 2 walks
slots (MissingKnob/KindMismatch); first failing check wins; sweep kind-check is
per-element, making resolve_axes a superset of GridSpace::new so the downstream
.expect() is infallible.
- Two iterations: single-run side, then sweep-axis side (shared name-resolution
core). The CLI sample stays nested (user's call); the worked example shows both
qualified and bare names honestly, no fixture change.
Auto-signed under /boss spec_auto_sign after the objective gates (precondition,
parse no-op, grounding-check PASS) and a unanimous five-lens spec-skeptic panel.
The panel hardened the spec across five rounds: it corrected the worked-example
names to the real param_space() output, pinned the exact-name match key, and made
the error precedence a total order closing a sweep per-element panic path. The
literal-inference grounding gap was closed separately by
|
||
|
|
e97906a0e1 |
test(aura-core): pin bare-literal Into<Scalar> kind inference
Authoring layers that lower raw literals via `impl Into<Scalar>` (the named-param-binding surface, spec 0030 / #35) rely on a bare integer literal inferring as i64 and a bare float as f64 — because i64/f64/bool are the only `From<…> for Scalar` impls, each literal class resolves to exactly one. This was true but untested: `from_widenings` uses suffixed literals (`7i64`), so a regression in the inference path would survive it. Behaviour-preserving pin of existing aura-core behaviour. Closes the one grounding gap the grounding-check flagged on spec 0030; also guards the property independently — adding a second integer `From` impl would now break loudly here rather than silently at a `.with("knob", 2)` call site. refs #35 |
||
|
|
654b23ad1f |
audit: cycle 0029 (#33) — run registry; one ledger drift fixed
Cycle-close architect review of 16e31fe..HEAD against docs/design/INDEX.md + CLAUDE.md. Regression gate: none configured (commands.regression: []), so the architect is the sole gate. What holds: - C18 honoured — aura-registry stores (manifest, metrics) RunReports append-only to runs/runs.jsonl, reproducible-from-manifest; no git/Gitea duplication, no multi-project manager. The depth deferrals (lineage, re-derive, run-diff, run_id, Aura.toml runs-dir) are named in the spec, not silently dropped. - C1 preserved — the sweep stays disjoint/lock-free; "sweep == N independent runs" now compares full RunReports; serde_json output is deterministic. - C12/C19/C20 fit — SweepPoint.report closes cycle C's deferred manifest-per- point gap; the manifest stays World-supplied (engine `sweep` is `Fn -> RunReport`, domain-free). - Workspace green (cli_run 11, registry 5, engine 93, …); clippy clean. Resolution: - FIX (this commit): docs/design/INDEX.md "External components" (the data-server row) still asserted the struck "external-dependency firewall / engine crates stay external-dependency-free" framing — stale after the C16 amendment (aura- core/aura-engine now link serde). Rewritten to the amended per-case policy. The iter-1 commit's "no ledger text still asserts it" missed this mirror; it is now true (re-grep clean across the ledger and live source). - FOLLOW-UP (#54, idea): RunReport has two divergent JSON encoders — serde for the on-disk store, hand-rolled to_json for stdout. This was a deliberate, spec-named deferral (keep stdout goldens stable this cycle); filed for the stdout→serde unification that retires the last hand-rolled writer. Ratify: the C16 amendment (blanket zero-dependency -> considered per-case policy) is the intentional contract change of this cycle, justified in the 0029 spec and the iter-1 commit; this audit ratifies it as drift-clean (the codebase no longer contradicts it anywhere). closes #33 |
||
|
|
5bded0ca83 |
feat(aura-cli): aura runs registry — persist on sweep, list + rank (cycle D / iter 3)
Final iteration of the run-registry cycle (#33): the read surface that makes a sweep family — and runs across separate invocations — comparable over time (C18's "compare experiments over time", which has no home in git or Gitea). - `aura sweep` now persists each point's RunReport to runs/runs.jsonl (append- only, one serde_json line per run) in addition to printing it. - `aura runs list` prints every stored record, in store order. - `aura runs rank <metric>` prints the stored runs best-first by the metric (total_pips desc; max_drawdown / exposure_sign_flips asc); an unknown metric is a usage error (exit 2), like every other aura arg error. sweep_report splits into a production sweep_family() (the pure run) and a #[cfg(test)] renderer kept for the in-bin goldens; the dispatch gains run_sweep / runs_list / runs_rank over default_registry() (runs/runs.jsonl under cwd). Process goldens run the binary in a temp cwd so persistence never dirties the repo; /runs/ is git-ignored as a backstop. Verified end-to-end: two `aura sweep` invocations accumulate 8 records; `aura runs list` shows all 8; `aura runs rank total_pips` orders them best-first; `aura runs rank bogus` exits 2 with the known-metrics hint. cargo test --workspace green (cli_run 11, registry 5, engine 93, …); clippy --workspace --all-targets -D warnings clean; no stray runs/ in the work tree. refs #33 |
||
|
|
2811c060dc |
plan: 0029 run-registry CLI persistence + read surface (iteration 3)
Final iteration of cycle D: aura sweep persists each point's RunReport to runs/runs.jsonl; aura runs list prints every stored record; aura runs rank <metric> prints them best-first. sweep_report splits into a production sweep_family() + a #[cfg(test)] renderer; process goldens run in a temp cwd so persistence never dirties the repo; /runs/ is git-ignored. refs #33 |
||
|
|
fe11cfea8a |
feat(engine,registry): SweepPoint carries RunReport; add aura-registry (cycle D / iter 2)
Iteration 2 of the run-registry cycle (#33): make the sweep family self-describing and add the registry store. Engine. SweepPoint.metrics (RunMetrics) becomes SweepPoint.report (RunReport), and the sweep closure bound is now Fn(&[Scalar]) -> RunReport. This closes the manifest-per-point gap cycle C explicitly deferred to #33 — each swept point now carries its full (manifest, metrics), the unit the registry indexes (C18). All engine-internal callers, the engine test fixture run_point, and the three sweep tests were rethreaded in the same iteration so the crate stays green; the CLI caller followed in the same change so `cargo test --workspace` is green again. CLI. The sweep_report closure builds a per-point RunReport (manifest params = param_space names zipped onto the point via scalar_as_param_f64; commit/window/ seed/broker as run_sample builds them) and prints via RunReport::to_json. The hand-rolled sweep_point_to_json/json_string/scalar_token are retired (the report renders itself), and the orphaned ParamSpec/SweepPoint imports dropped. Net: aura run, aura sweep, and (next iteration) aura runs all print the same RunReport shape. The two aura sweep goldens now pin the per-point manifest params, NOT the commit (it is the real git HEAD, volatile). Registry. New crate aura-registry: Registry::{open, append, load} over an append-only runs/runs.jsonl (one serde_json line per RunReport), free rank_by (best-first per metric — total_pips desc, max_drawdown/exposure_sign_flips asc), and RegistryError (Io / Parse{line} / UnknownMetric). Five unit tests cover append->load round-trip, missing-file = empty, corrupt-line = Parse{line}, per-metric ranking, and unknown-metric error. Built and unit-tested; NOT yet CLI-wired (aura sweep persistence + aura runs list/rank are iteration 3). Verified: cargo test --workspace green (aura-registry 5, aura-engine 93, aura-cli 7+8, others unchanged); clippy --workspace --all-targets -D warnings clean; aura run stdout shape unchanged; aura sweep now emits full RunReports. refs #33 |
||
|
|
5fa003da64 |
plan: 0029 run-registry engine + registry (iteration 2)
Iteration 2 of cycle D: SweepPoint carries a full RunReport (closure bound -> RunReport), all engine + CLI callers rethreaded in lockstep so the workspace returns green; the hand-rolled sweep_point_to_json retired (each point prints via RunReport::to_json); new aura-registry crate (open/append/load/rank_by + RegistryError), built and unit-tested but not yet CLI-wired. The aura sweep goldens pin the per-point manifest params, not the volatile git-HEAD commit. refs #33 |
||
|
|
eec876975c |
feat(engine): serde foundation + amend C16 dependency policy (cycle D / iter 1)
Iteration 1 of the run-registry cycle (#33): lay the dependency + serde foundation the registry's typed read-path needs, and amend the contract that forbade it. Contract change (load-bearing — C18/C16). C16's blanket "zero-external- dependency by commitment" + "aura-ingest is the sole external-dependency firewall" framing is struck and replaced by a considered, per-case dependency policy: dependencies are admitted by deliberate review (what a crate pulls in vs. what it buys), with particular scrutiny for anything entering the frozen deploy artifact (C13); standard vetted crates (serde, rayon, ...) pass that review and are used wherever they do the job, including in the bot; hand-rolling what a vetted crate does is the anti-pattern. C16's engine/project split + three-tier node reuse are unchanged. The five source comments that asserted the struck framing (aura-engine/aura-ingest Cargo.toml + aura-ingest/report.rs docs) are rewritten to match; no live source or ledger text still asserts it. Per-case justification for serde (the policy now requires one): it gives the run-report types a typed (de)serialization path — exactly what ranking/compare over stored runs needs, and what the writer-only hand-rolled JSON (C14) could never provide; closes the typed-read-path gap (#17). Its closure is tiny, ubiquitous, heavily audited, and its output deterministic (C1-safe). serde_json is test-only this iteration (dev-dependency); no production serde_json yet. Changes: [workspace.dependencies] serde (derive) + serde_json; serde derives on Timestamp (aura-core) and RunMetrics/RunManifest/RunReport (aura-engine), with serde_json round-trip tests (window renders as a [from,to] integer array via the transparent Timestamp newtype). The hand-rolled to_json writers are untouched (still drive stdout). No aura-registry crate, no SweepPoint change — those are iterations 2 and 3. Verified: cargo test --workspace green (incl. the two new round-trip tests); clippy --workspace --all-targets -D warnings clean; no surviving assertion of the struck zero-dep framing in live source or the ledger. refs #33 |
||
|
|
8159b785a8 |
plan: 0029 run-registry foundation (iteration 1)
Iteration 1 of cycle D: amend C16 (blanket zero-dep -> per-case policy) in the ledger + the five source comments asserting the struck framing; add serde/ serde_json workspace deps; derive Serialize/Deserialize on Timestamp and the RunMetrics/RunManifest/RunReport report types, with serde_json round-trip tests. No CLI change, no aura-registry crate, no SweepPoint change (those are iter 2/3). refs #33 |
||
|
|
d309336a72 |
spec: 0029 run-registry — index & compare a sweep family
Cycle D of the milestone "The World — parameter-space & sweep" (#33). A persistent, append-only run registry: each run's (manifest, metrics) is a record in runs/runs.jsonl; a read surface lists all records and ranks them best-first by a named metric — C18's "compare experiments over time", which has no home in git or Gitea. First cut = persist + list + rank; promotion/ status, lineage, run-diff, on-demand re-derivation, run_id, and Aura.toml runs-dir wiring are deferred. Contract-level change: C16's blanket "zero-external-dependency by commitment" is amended to a considered, per-case dependency policy (scrutinize what enters the frozen artifact; standard vetted crates like serde/rayon pass that review; never hand-roll what they do). The engine/project split + three-tier node reuse of C16 are unchanged. This admits serde/serde_json — the typed read-path ranking needs, and which closes #17 (typed RunReport handle). Shape: serde derives on RunManifest/RunMetrics/RunReport + Timestamp; SweepPoint carries a full RunReport (closing the manifest-per-point gap cycle C deferred to #33); new crate aura-registry (open/append/load/rank_by); CLI gains aura runs list / aura runs rank <metric> and aura sweep persists each point. Three iterations, first planner handoff = iteration 1 (foundation). Sign-off: spec_auto_sign was enabled for this cycle; the five-lens spec-skeptic panel returned 3 SOUND + 2 BLOCK (rank-ordering semantics; iteration-cut compile-ordering + Scalar->f64 coercion). Both BLOCKs were genuine defects, fixed before sign-off (rank is now best-first per-metric; the closure-type change lands with its callers in one green iteration; the coercion is shown). Non-unanimous panel routed the spec to human sign-off, granted by the user. refs #33 |
||
|
|
728ca1a2d2 |
chore(boss): enable spec auto-sign for this project
The orchestrator may now sign a spec in the user's place under /boss, but only through specify's Step-6 auto-sign gate: all objective gates green (precondition, parse, grounding-check PASS) AND a unanimous five-lens spec-skeptic panel. The human signature stays mandatory on any non-PASS path. |
||
|
|
16e31fe05d |
audit: cycle 0028 (#32) — drift-clean; param-sweep grid (the World, cycle C)
Architect drift review over 776bd54..HEAD: drift-clean on every load-bearing contract. - C1: sweep()'s disjointness is real — sweep_with_threads shares only an AtomicUsize cursor, tags results by point index, sorts post-join; the family order is the enumeration order, thread-count-independent (pinned by family_is_deterministic_across_thread_counts at 1 vs 8 vs derived-N). Each point builds fresh, shares nothing mutable. First realization of "parallelism across sims, never within one". - C16: zero-external-dependency intact — crates/aura-engine/Cargo.toml unchanged, no dependency added; execution is std::thread::scope, never rayon. No serde. - C12: scope discipline held — only the C12.1 grid axis landed; random / optimize / walk-forward / MC and per-point manifest assembly are absent and deferred per spec. - C19/C20: untouched — sweep drives bootstrap_with_params per point, no topology mutation. - C14: the CLI's sweep_point_to_json mirrors RunReport::to_json's hand-rolled token rules byte-for-byte; no second JSON dialect. C8/C18: metrics-drain stays author-side in the closure (the engine ↔ harness boundary the design intends). Regression: no scripts configured (profile regression: []) — architect is the sole gate, and it is green. Debt (filed, blocks nothing): - #53 — the SMA-cross sample harness topology is now duplicated across three sites (blueprint.rs test, sweep.rs test, cli main.rs production) with no test pinning them equal. Pure tidy, carry-on. Deferred follow-up filed: - #52 — random param-sweep enumeration (C12.1's other half: ranges + sample-count + seeded RNG), the deliberate scope cut from this cycle's design. Cycle 0028 closes #32 (param-sweep grid, engine primitive + aura sweep CLI demonstrator). Milestone "parameter-space & sweep" is NOT closed by this audit (that needs the milestone fieldtest, a separate manual act); #33 (run registry, cycle D) remains open in the milestone. closes #32 |