1102d776df77d479202bf3458c20dfdc0337e35f
926 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1102d776df |
feat(runner, cli): runner-side marker literals + the zero-trade walk-forward notice
Iteration stderr-markers-2, tasks 3-4 of the stderr-class-markers plan. aura-runner's four continuing-run diagnostics join the class grammar as hand-written literals (the #295 boundary keeps the macro owner in the presentation crate): the stale-dylib warning gains the `aura: ` namespace, and the three tap/no-nominee notices become `aura: note: ` — the latter two extracted into pure note-builder fns with unit pins. The two e2e pins on the cost-model tap string moved with the retag (research_docs.rs). A new project_nodes e2e exercises the real stale- dylib load path: fresh build emits nothing, a source newer than the dylib emits exactly one class-marked warning and still succeeds. The #313 notice ships on both walk-forward paths: `aura: note: all N walk-forward windows recorded zero trades` before the summary JSON, exit 0 untouched (a null result is a valid research result, #198). Wording and condition live in one shared diag helper taking the per-window trade counts (orchestrator tidy of the reviewer's duplicated-predicate residue); the call sites keep only the path-specific field extraction. Three e2es pin it: the equal-length degenerate on the synthetic path (exactly one note, no warnings, exit 0, summary unchanged), a traded negative twin, and the real/sugar-path degenerate gated on the GER40 archive. refs #278, refs #313 |
||
|
|
34987be389 |
feat(cli): stderr class markers — diag module, campaign/verb-sugar retags
Iteration stderr-markers-1, tasks 1-2 of the stderr-class-markers plan (spec signed via grounding-check PASS, decisions logged on #278). The two stderr diagnostic classes become machine-separable (refs #278): a new crate-internal diag module owns the grammar as note!/warning! macros (`aura: note: <text>` benign continuing-run diagnostic, `aura: warning: <text>` recorded fault the run survives); the four undifferentiated sites are retagged — gate-emptied cell -> note, failed cell / walkforward cell / mc cell -> warning — and the four scaffold literals migrate onto the shared macro (output bytes unchanged). The always-on record summary line gains the bare `aura: ` namespace it was missing; bare-prefix stays the vocabulary for error lines accompanying a non-zero exit (C14 partition) and plain info lines. Exit codes unchanged throughout. Verification: RED->GREEN warning pin on the per-cell-fault e2e; new gate-emptied e2e (impossible n_trades threshold) pins the note marker, exit 0, and the summary-line prefix; scaffold e2es and the full workspace suite green; clippy -D warnings clean on aura-cli. The C28 shell-module roster test gained the diag.rs entry. Known residue for the next slices: aura-runner literals (stale-dylib warning, three tap/no-nominee notes) and the #313 zero-trade notice follow in tasks 3-5. The verb_sugar warning call sites remain e2e- uncovered (a hostless walkforward fault fixture is structurally unreachable — single-instrument validation refuses before the cell loop); their grammar is pinned via the shared macro and the campaign warning e2e. refs #278 |
||
|
|
cfc2d9f5b1 |
feat(core): doc_gate refuses the namespaced short-name alibi (GREEN)
The fieldtest's spec_gap closed per the #316 decision (tighten, not ratify): doc_gate normalizes the doc against both the full entry name and its bare display segment after the last :: -- an extension node ns::Scale with doc "Scale" now refuses (RestatesName), matching the register seam's effective bare-name behaviour. Unnamespaced entries are unchanged (the checks coincide); genuine meaning lines containing the name as a substring stay accepted (equality, not containment). The C29 contract sentence carries the precision. Gates: workspace green (98 suites, 0 failed -- incl. the C29 coverage walk over every shipped doc under the tightened gate), clippy clean. refs #316 |
||
|
|
b04b56148d |
test(core): RED -- doc_gate refuses the namespaced short-name alibi
Executable spec for the fieldtest spec_gap (decision on #316: tighten, not ratify): doc_gate("lab316_nodes::Scale", "Scale") must be Err(RestatesName) -- a doc normalizing to the final ::-segment of a namespaced entry name restates the name. Unnamespaced entries and genuine meaning lines containing the name stay Ok (guard assertions green). Currently red: the shipped check compares the full name only. refs #316 |
||
|
|
96e79184e0 |
fix(docs, registry): fieldtest follow-ups -- guide sync, doc-op documentation, surface-neutral refusal
Three of the four immediate fieldtest findings for the self-description cycle: - authoring-guide S0: the worked Scale literal gains the required NodeSchema.doc line (+ the C29 comment) -- the shipped example was a hard E0063 for anyone pasting it (the exact #314 teaching-failure class, caught from the consumer seat). - The op table becomes "The nine ops": the C29 doc op is documented with its JSON shape, the register requirement, the at-most-once rule and the gate; the README op-kinds list names it too. The fieldtester had to guess the shape from commit bodies -- now it is on the public surface. - The store-gate refusal's fix hint is surface-neutral: builder .doc(...), blueprint "doc" member, or op-script {"op":"doc"} -- previously it prescribed the Rust builder to a JSON author. Gates: registry, graph_construct (63) and project_load (14) suites green; refusal-prose pins unchanged (pinned substrings untouched). refs #316 |
||
|
|
2d988fdf3d |
fieldtest: 316-self-description — 4 examples, 1 bug / 3 friction / 1 spec-gap
Consumer-seat run over all four C29 seams (load, register, op-script doc, document description) from the public interface only. The three working findings verify the #314 failure class removed end to end; the findings route: guide sync + refusal-prose neutrality + doc_gate short-name parity this run, discoverability surfaces to the tracker. refs #316 |
||
|
|
bd7585b609 |
audit(cycle 316): drift disposition, bench seed docs + ratified re-pin, C29 scope paragraph
Cycle-close audit for the self-description cycle. Architect: drift_found (mild), core holds -- the gate is pure string shape in core imported by every seam (C28), the engine never evaluates description text (C17/inv 10), determinism pins untouched (C1); the store boundary is gated by construction with the raw write private; Op::Doc is a closed typed construct (C25). Regression: aura-bench green (exit 0) after one cycle-caused infra fix and one ratified re-pin. Infra fix: the bench campaign seeds (BP_SMA_A/B) predate C29 and were refused by the store gate (exit 2, infrastructure-first rule) -- both thread the authored sma_signal meaning line. Ratify: cli_fixed_cost's run_line_fnv moved because bench_sma_a.json now carries its doc -- the blueprint's content id shifts exactly as the spec anticipates for fixtures gaining docs; campaign result fingerprints are unchanged (descriptions stay out of execution, C29). Baselines re-pinned via aura-bench pin (throughput baselines re-pinned in the same sweep, all deltas within tolerance at pin time). Drift disposition: - [high] C29 guarantee overclaim vs emit/binding-column vocabularies -> contract now carries an explicit Scope paragraph naming the residue, tracked as #321 (also covers the [medium] non-roster schema walk gap). - [low] Delay's meaning line cited the stale C5 for the delay register; corrected to C9. refs #316, refs #321 |
||
|
|
9c7f60b269 |
docs(ledger): C29 self-description contract -- INDEX entry + domain invariant 13
The cycle's design principle enters the ledger: every closed-vocabulary entry the binary exposes carries a one-line meaning behind one shared deterministic shape gate (doc_gate) at three seams -- compile/unit for engine-shipped entries, load for native node crates, register for the content-addressed store (documents: an additive-optional gated description). Forbids the engine evaluating description text (C17 / invariant 10), any influence on execution/identity/determinism (C1), retroactive invalidation of registered artifacts, and machine-invented meaning lines. Why: the audience is headless LLM agents (#319); field evidence #314 showed schema knowledge being recovered by CAS forensics from the release binary -- the removed failure class. All spec acceptance criteria are now met across the four iterations of this cycle (core carrier + std texts; domain threading; load seam; register seam + op-script doc slot + document description). closes #316 |
||
|
|
7126886b81 |
feat(engine, cli): op-script doc slot -- the vocabulary grows the C29 meaning line
Iteration 3 completion (tasks 5-6): the construction vocabulary gains the slot its own #125 scope-cut comment anticipated. Op::Doc { text } is the op-script twin of the builder's .doc(...) -- a closed, typed metadata construct (C25, no logic content); at most one per script, a second refuses via OpError::DuplicateDoc (the existing duplicate-fault family), rendered "a doc op may appear at most once". GraphSession threads the text through Composite::with_doc at finish, so op-built composites pass the C29 store gate like builder-built ones. The op-script register test runs on the described variant (SIGNAL_DOC_DESCRIBED) for both shapes; the doc-less script now pins the C29 refusal, and the E2E phase added the RestatesName op-script case. Full gates: cargo test --workspace green (98 suites, 0 failed); clippy -D warnings clean. refs #316 |
||
|
|
593e233e66 |
test(cli): C29 register-refusal e2e -- doc-less root, doc-less nested, described twin
Iteration 3 task 4: register_refuses_undescribed_composites_end_to_end pins the store gate's verb surface -- a doc-less root refuses naming `sma_signal` and the rule, a doc-less named nested composite refuses naming `inner`, the described shipped example registers (exit codes and stderr through the C14 contract). The pre-existing twin pin graph_doc_field_moves_content_id_but_not_identity_id stays green. Checkpoint note: the loop escalated this task spec-ambiguous because its plan gate demanded a fully green graph_construct suite while the op-script register test is known-open until the doc-op task lands (a plan-sequencing defect, orchestrator's authoring error); the task's own work was verified green and is committed here, the doc-op task follows. refs #316 |
||
|
|
750ee93180 |
feat(research, registry, cli, runner): C29 register seam -- description gate, store-boundary doc gate, authored root docs
Iteration 3 of the self-description plan, tasks 1-3 (checkpoint commit
mid-iteration; the register e2e, the op-script doc slot, and the full
gates follow):
- aura-research: DocFault::BadDescription { subject, fault }; a present
document description passes doc_gate in validate_process /
validate_campaign (absent is never a fault and stays byte-identical);
prose arm in doc_fault_prose; tests incl. content-id participation.
- aura-registry: the store boundary gates admission -- the public
put_blueprint parses the canonical bytes and walks root + named
nested composite docs (None refuses like ""), refusing via
UndescribedComposite { name, fault } / MalformedBlueprint; the raw
write survives as private put_blueprint_unchecked (store-mechanics
tests + the never-retroactive witness: doc-less pre-C29 entries stay
readable). Registry-internal seeders triaged junk-vs-real.
- authored root docs: the 4 example blueprints + their 4 open-fixture
twins, the scaffold starter template, the scaled_signal and hl_signal
inline fixtures, and the three programmatic member.rs builders
(breakout, meanrev, hl_channel) via GraphBuilder::doc -- mechanical
propagation of the authored lines.
Recovery note: the implement run's task 3 discarded tasks 1+2's edits
in three files via a file-level checkout (the issue-#23 guard shape);
the loop's boundary snapshot caught it and the content was restored
from recovery snapshot 11a6ae2, then re-verified (research 81 green,
registry 51 green, cli 58/59).
Known-open at this checkpoint: the op-script register test is red --
op-built composites have no doc surface (#125 scope cut); the op
vocabulary grows the anticipated doc slot in the next task of this
iteration (decision logged on the issue).
refs #316
|
||
|
|
d6694d0641 |
feat(runner, cli): C29 load seam -- undescribed extension vocabulary refuses at load
Iteration 2 of the self-description plan: field presence is compile-
enforced since iteration 1, but an extension crate can still ship the
empty-string alibi (or a name-restating doc) the compiler cannot see.
load_crate now walks every charter-checked type id, resolves its
builder, and runs aura_core::doc_gate over schema.doc; a fault refuses
the load via ProjectError::UndescribedVocabularyEntry { type_id, fault }
(exit 1 through the CLI's Display-driven surface, C14).
The variant carries the DocGateFault beyond the spec's minimal sketch
because the spec's error-handling section requires the refusal to name
entry + field + rule -- with only the type id the prose could not
distinguish the two faults (decision logged on #316).
Two gate-failing fixture crates drive the e2e, modelled on
badcharter-project: undescribed-project (und::Opaque, doc: "") for the
Empty arm, restated-project (restated::Echo, doc: "Restated Echo") for
the RestatesName arm -- the second contributed by the E2E phase to
close the only-unit-pinned gap on that arm. A runner unit test pins
both rendered Display arms directly. The described twins (demo-project,
nested-nodes-project) stay green and are now load-bearing for the gate.
Gates: cargo test -p aura-cli --test project_load (14 green, incl. both
refusal e2es); cargo test --workspace green; clippy -D warnings clean.
refs #316
|
||
|
|
ab3f16879b |
feat(runner, cli, research): metric/tap schema carriers + the C29 coverage walk
C29 compile/unit seam, tasks 6-10 -- iteration 1 of the self-description plan complete: every engine-shipped vocabulary entry now carries a gate-clean one-line meaning. - aura-runner: the internal tap recording sink threads its doc (the last unthreaded NodeSchema site) -- first full workspace build since the field became required. - aura-cli scaffold: the sample node's template carries a doc line plus a maintenance comment, so every scaffolded extension crate starts gate-clean; both extension-vocabulary fixture exemplars thread real meaning lines (the load seam walks these in iteration 2). New E2E: the scaffold's own doc must pass its own gate -- an alibi doc in the template would teach the wrong pattern to every new project, and the compiler checks only the field's presence, never its shape. - aura-research: metric_vocabulary()/tap_vocabulary() promote from bare name arrays to MetricSchema/TapSchema carriers (id + doc, 17 + 4 authored lines); callers adapt to .id with byte-identical output (introspection goldens unchanged). The #190 guard keeps its triple -- the doc column adds no fourth roster site. - Coverage: tests/self_description.rs walks all five vocabularies (blocks, metrics, taps, folds, std nodes) through doc_gate -- the guard that keeps any future edit from blanking a doc. Adjudicated review residue: the 17 metric doc strings are surfaced by no CLI path yet -- deliberate; task 8 pins callers byte-identical, the surfacing belongs to the CLI self-description work (#315). Plan-cited coordinates lib.rs:1003/:1283 resolved to the real caller sites (:1019/:1299); the plan's literal test code was reconciled to the real accessors (FoldRegistry::core().roster(), Env::std()). Gates: cargo test --workspace green (0 failed, incl. the new coverage walk + scaffold E2E); cargo clippy --workspace --all-targets -- -D warnings clean. refs #316 |
||
|
|
d8c6938027 |
feat(engine, market, strategy, backtest): NodeSchema.doc threading across the domain crates
C29 compile/unit seam, tasks 4+5 of the self-description plan. aura-engine (task 4): derive_signature stamps doc: "" with the stance recorded in-code -- a derived composite signature is graph wiring, not a vocabulary entry; no seam walks its doc, the described surface is the composite's own doc at the register seam. All in-crate test literals thread doc: "test-only schema". Domain crates (task 5): the 12 production builder sites in aura-market / aura-strategy / aura-backtest carry authored meaning lines; test sites in aura-backtest / aura-composites / aura-ingest thread the test-only doc. Three texts were corrected against the actual node semantics after quality review rather than kept from the first authoring pass: SimBroker is the frictionless integrator of held exposure times price return into cumulative pip equity (no fills/stops/lifecycle -- that is PositionManagement's domain), the shared cost-node line names the PM-geometry inputs and both charge modes (AtClose / PerHeldCycle) instead of a "per-trade gross R" mapping, and LongOnly's line conditions on its enabled param and speaks port-term "exposure". Gates: cargo test green for all six touched crates (engine, market, strategy, backtest, composites, ingest); the workspace-wide gate follows task 6 (aura-runner is the one remaining unthreaded site, E0063 by design until then). refs #316 |
||
|
|
a32dc38d18 |
feat(std): meaning lines for all 27 shipped node schemas
C29 compile/unit seam, task 3 of the self-description plan: every aura-std NodeSchema literal threads its one-line doc. Four texts were corrected against the actual eval/finalize semantics rather than taken from the plan table verbatim: Delay is a lag-N register (not one-step), GatedRecorder flushes an ungated final row at finalize, Latch is a level-sensitive set/reset register (captures no input), SeriesReducer emits its single summary row at finalize (not per cycle). Gate: cargo build -p aura-std --lib clean; full std test run follows at the all-crates gate once strategy/backtest/engine thread their sites (the earlier per-crate test gate was unsatisfiable in isolation -- std's dev-deps pull crates whose sites belong to later tasks). refs #316 |
||
|
|
df3c2f06bd |
feat(core): doc_gate shape check + required NodeSchema.doc field
C29 compile/unit seam, core carrier (iteration 1 of the self-description
spec): DocGateFault { Empty, RestatesName } + doc_gate() — deterministic
string-shape check, never content judgement — and NodeSchema gains the
required pub doc: &'static str, so every construction site must declare
a meaning line (compiler-enumerated). Core's own 10 test literals thread
doc: "test-only schema"; doc_gate/DocGateFault re-exported at crate root.
Downstream crates are E0063-broken by design until their sites thread
the field (tasks 3-7 of the plan); gates here are crate-scoped:
cargo test -p aura-core (59 green) + clippy -p aura-core (clean).
Interim subset commit of the implement run after adjudicating a
cross-task discard: task 2's file checkout had destroyed task 1's
node.rs/lib.rs work; restored from the loop's recovery snapshot
53c55c3 and merged with task 2's field threading.
refs #316
|
||
|
|
8c19260e8d |
fieldtest: tap-subscribers — 5 examples, 0 bugs / 2 friction / 3 spec-gap
Cycle-close fieldtest for #283 + #77 (tap subscribers), exercising the new
tap surface as a downstream consumer over the public interface only (design
ledger, glossary, cargo doc, example corpus — never the crate sources).
Five examples, one per axis, all built and run against worktree HEAD
|
||
|
|
bd0c557f16 |
docs(ledger, glossary): tap-subscribers cycle tidy — C8/C27/C28 lifts, tap subscription vocabulary
Cycle-close audit for the #283/#77 tap-subscribers cycle (09994b8..6e3f394). Architect review: code contract-clean; what holds — C1/C7/C8 (empty-output caller-built consumers, (Timestamp, Cell) zero-heap payload, lifecycle hooks inside the sequential loop, byte-identical twin-run and streamed-vs-legacy byte-equality pins), C28 (assembly-crate placement of the writer-holding consumer, c28_layering guard green), C25 (registry growth = new Rust entry, data-expressible Named form, roster-enumerating refusals). All drift items were doc lifts, resolved in this commit: - C8: the lifecycle is now a symmetric pair — initialize() as finalize's start-of-stream mirror (infallible by signature; acquire-in-initialize, degrade-to-inert, surface-once-at-finalize); history sidecar records the refinement and the #77 resolution the old Deferred paragraph still carried as open. - C27: Guarantee and Current state now describe the shipped model — tap plan (Named{label, params} | Live) over the layered fold registry, BOTH entry points (run_signal_r AND run_measurement) on the one shared wiring pair, record streaming at constant memory. - C28: aura-runner now defines one graph node of its own (the in-graph record consumer — exactly the graph-meets-persistence assembly position); the #297 refusal-site count grows ~20 → ~24 (the four tap-plan refusals, already typed before the exit). - glossary §tap: the subscription/fold-registry sense added; Avoid line steers probe/monitor/scope away from the observation slot. Bench (report-only, loadavg 5.8 > 2 on the box — read with care), all fingerprints OK, no regression: engine_throughput bars_per_s 14005262 wall_s 0.714s Δ bars_per_s +3.0% wall_s -3.0% ingest_throughput bars_per_s 12970670 wall_s 0.387s Δ bars_per_s +0.5% wall_s -0.5% campaign_sweep cpu_percent 2202.0 peak_rss_mb 92.2 wall_s 1.396s Δ cpu_percent +1.5% peak_rss_mb -6.1% wall_s -1.5% campaign_heavy cpu_percent 2155.0 peak_rss_mb 94.8 wall_s 5.585s Δ cpu_percent +1.8% peak_rss_mb -3.5% wall_s -0.4% cli_fixed_cost help_ms 1.5ms run_ms 3.6ms Δ help_ms -1.8% run_ms -2.6% No baseline update (report-only surface; no ratify due). Correction on 6e3f394's body: "no Probe symbol is introduced anywhere" — introduced is the operative word; the pre-existing aura-core test fixture `struct Probe` stays, as the cycle's design record specified. Follow-ups on the tracker: #308 (legacy Recorder surface migration), #307 (binary trace format). Cycle spec/plan working files discarded. refs #283 refs #77 |
||
|
|
6e3f394b48 |
feat(aura-runner, aura-cli): run_measurement on the shared tap pair; integration + register coverage (closes #283, closes #77)
Completes the #283 registry cycle. run_measurement drops its duplicated Recorder-bind + post-run try_iter drain for the shared bind_tap_plan/BoundTaps pair — the mirror of run_signal_r is now the same code, so the two declared-tap entry points cannot drift; the measure CLI arm passes TapPlan::record_all() (semantics unchanged, constant memory). Measure-side library tests pin a fold plan's one-row trace (streamed mean bit-equal to the slice mean over the synthetic prices) and the tap-free write-nothing guarantee through the shared pair. Run-side integration tests pin the #283 acceptance surface: fold and live plans agree exactly with the recorded series of the same blueprint (C1, three runs, three drain policies); a live-only plan persists nothing; plan refusals (unknown tap, roster-enumerating unknown label) fire typed before any store I/O; two identical record-all runs produce byte-identical tap files. The previously untested `aura: writing tap traces failed` register gains end-to-end coverage on both arms: store root occupied by a file (pre-run refusal) and a read-only run dir (deferred initialize-open failure — the run completes, the failure surfaces terminally, no index.json is written, the store's treat-as-absent crash shape). Two forced deviations from the plan's scripted literals, both verified equivalent: an explicit match replaces expect_err (BoundTaps is deliberately not Debug — it carries a TraceStreamer), and the test helper takes &Path, not &PathBuf (clippy::ptr_arg under -D warnings). Closes #77 as specced: zero per-cycle heap on the tap path ((Timestamp, Cell) payload, committed earlier this branch) and the Recorder→Probe rename retired (no Probe symbol anywhere; decision recorded on #77, 2026-07-21). Verification: cargo test --workspace — 1517 passed, 0 failed; cargo clippy --workspace --all-targets -- -D warnings clean; tap_recording 5/5 (byte-compat anchors + both register arms). refs #283 refs #77 |
||
|
|
d8ef9de5c2 |
feat(aura-runner): TapPlan + layered fold registry; run_signal_r re-wired onto the shared pair
The #283 registry model's assembly seam: tap_plan.rs carries TapSubscription (Named{label, params} — fully data-expressible — | Live(closure), the single non-data variant), TapPlan (name → subscription + default; the plan travels with the FoldRegistry it resolves against, the Env-at-resolve pattern, so layered hosts inject vocabulary as data), and the registry itself: eight core entries (record, count, sum, mean, min, max, first, last), each with a doc line (help + roster-enumerating refusals) and a scalar-typed param schema (all v1 entries param-less; the seam ships now because it sits in every entry's build signature). Typed TapPlanError refusals fire pre-bootstrap, before any store I/O; the terminal Store variant keeps the established "writing tap traces failed" register bytes. bind_tap_plan/BoundTaps is the shared wiring pair both declared-tap entry points call; run_signal_r is re-wired onto it here (Recorder bind + post-run try_iter drain replaced — no O(cycles) retention, no per-cycle heap), with all six callers threaded (both CLI run-arm and the four example-identity test calls pass TapPlan::record_all()). run_measurement follows in the next commit. Registry growth stays a new Rust entry (C25); a layer-registered entry is pinned by the injection test, param validation by a schema-carrying test entry (missing/mismatched/unknown all refused with named messages). Verification: cargo build --workspace clean; aura-runner 85+ tests green incl. both example-identity anchors; tap_recording byte-compat anchors green (recorded traces byte-identical, tap-free writes nothing, duplicate-tap refusal unchanged); clippy -D warnings clean. refs #283 refs #77 |
||
|
|
92e281e4ec |
feat(aura-registry, aura-runner): deferred writer opener; TapRecorder moves in-graph to the assembly crate
Two #283 groundwork moves for the fold-registry model: - TraceStreamer::register_tap returns a TapWriterOpener — everything needed to open one tap's incremental writer later, from inside a consumer node's initialize, without holding the streamer. tap_writer is re-expressed as register_tap(..).open(), so there is exactly one open path; pinned by a byte-equality test between the two. - TapRecorder leaves aura-std (which must not know aura-registry, C28 layering) and is reworked in aura-runner as the record entry's consumer: it holds the TapTraceWriter in-graph — initialize opens (deferred; an open error is stored and the node degrades to inert), eval appends per fired warm cycle (zero heap, no thread, no channel), finalize finishes the writer and reports exactly one terminal outcome. The SyncSender-based aura-std variant and its planned writer-thread protocol are retired; newest_cell becomes pub for the assembly crate. Verification: cargo clippy --workspace --all-targets -D warnings clean; aura-core/engine/registry/std/runner suites green (new tests: opener equivalence, happy-path capture + single Ok outcome, failed-open inert + single Err outcome). refs #283 refs #77 |
||
|
|
b8ba324a41 |
feat(aura-core, aura-engine): Node::initialize — the start-of-stream mirror of finalize
Adds the second lifecycle hook the #283 design revision pairs with finalize: Node::initialize (default no-op) runs once per node, in topological order, before the first source value — inside the deterministic sequence, no within-sim concurrency (C1). Consumers acquire run resources there (the record consumer opens its streaming writer); the hook is infallible by signature, so an acquiring node stores its error, degrades to inert, and surfaces the failure once, terminally, at finalize. Every existing node keeps the default. Pinned by the mirror test run_initializes_every_node_once_before_the_stream (init0,init1 strictly before any eval, exactly once), the shape of the existing finalize test. RED was observed by temporarily reverting the flush (test fails with evals-before-inits), then re-applied. Ledger note for cycle close: C8's lifecycle section gains initialize as finalize's mirror. refs #283 |
||
|
|
4ae2297a35 |
feat(aura-registry): streaming trace write path — begin_run, TraceStreamer, TapTraceWriter
Adds the incremental write path the tap-subscriber cycle streams record subscriptions through: TraceStore::begin_run opens a run directory and returns a TraceStreamer; per tap, TapTraceWriter appends (ts, cell) points into two temp column streams and finish() assembles the canonical ColumnarTrace JSON by streaming concatenation (io::copy), removing the temps. index.json is written last by TraceStreamer::finish, preserving the store's crash discipline (no index -> NotFound, treat-as-absent). Compatibility is pinned as an equality property, not a frozen byte assumption: a new test asserts the streamed output byte-identical to TraceStore::write's for the same rows (including empty and bool columns), so the two write paths cannot drift and every reader (read, read_family, chart) stays untouched. TraceStore::write is refactored onto shared write_tap_file/write_index helpers used by both paths. Temp streams open read+write (OpenOptions), since finish() seeks back and re-reads them. Verification: cargo test -p aura-registry — 78 passed (3 new: byte-equality incl. empty+bool, crash shape NotFound, caller-order index). refs #283 |
||
|
|
09994b83ed |
feat(aura-std): tap subscriber sinks — TapFold, TapRecorder, TapLive
The three C8 sinks realizing the per-tap subscription model (#283): TapFold (closed FoldKind vocabulary Count|Sum|Mean|Min|Max|First|Last, in-graph accumulation, exactly one finalize row), TapRecorder (bounded SyncSender<(Timestamp, Cell)>, lossless backpressure — a full channel blocks the sim, wall time never state), and TapLive (consumer-owned closure, inline on the sim thread). All three move (Timestamp, Cell) — 16 B, Copy — instead of (Timestamp, Vec<Scalar>): zero per-cycle heap on the tap path (#77 part 2). The shared kind-dispatched newest-cell read lives in tap_cell.rs. Known-good subset commit of iter tap-subscribers (tasks 1-3 of 7, each gated implementer -> spec-compliance -> quality DONE; the loop blocked on a task-4 plan defect, repaired separately): the trace-store streaming writer, the TapPlan wiring seam, and both entry-point rewires follow in this same cycle. Verified directly: cargo test -p aura-std — 109 passed, 0 failed. refs #283, refs #77 |
||
|
|
8688a60ded |
docs(ledger): split the design ledger into an INDEX map, per-contract live files, and history sidecars
The single-file ledger had grown to 2968 lines / ~42k tokens, mixing current design law with accreted history: 59 cycle-stamped realization blocks, 18 [HISTORY] passages, 22 supersession markers, and the C10 / C22 / C24 reframe sagas layered several supersessions deep. A code-grounding audit (31 agents, adversarially verified) confirmed 11 defects stated as current truth: stale crate homes from the C28 #288 roster split (cost nodes, PositionManagement, PositionEvent, Session), the renamed InputSpec->PortSpec, the pre-#241 project model in C16 and the open-threads section, a stale HarnessKind retirement deferral in C24, and three C28-internal inconsistencies. New shape, per the ailang precedent: - INDEX.md stays the sole addressable entry point: foundation, external components, a C-id-keyed contract map (one line per contract), and only the genuinely open architectural threads. - contracts/cNN-<slug>.md carries each contract's current truth only: Guarantee / Forbids / Why with ratified refinements integrated, plus a code-anchored Current state. All confirmed defects are fixed here; crate anchors were re-verified against the tree. - contracts/cNN-<slug>.history.md (18 sidecars) and INDEX.history.md preserve every superseded block verbatim, stamps and issue refs intact, under a frozen-record banner. Nothing was deleted: superseded design intent remains an addressable working-tree artifact, off the per-cycle audit walk. - Ledger discipline is now stated in INDEX.md: live files are edited in place at cycle close, superseded text moves verbatim to the sidecar, and a supersession marker in a live file is itself an audit finding. Every contract file was verified against its old text by an independent zero-loss pass (statement-by-statement) plus a code-accuracy spot check; C-ids and contract titles are unchanged, so existing C-id citations in code, tests, and issues resolve as before. |
||
|
|
fb28b831f3 |
feat: show resolves a unique content-id prefix (closes #302)
aura {process|campaign} show now accepts the 8-hex prefix every surface
prints: an exact match is tried first, then the prefix is resolved
against the store's candidate ids — a unique match prints the identical
stored bytes as the full id (#164 byte discipline), an ambiguous prefix
refuses listing every sharing candidate, and a genuinely-unknown id
keeps its established refusal. The resolution mirrors the semantics
reproduce shipped with #298, closing the asymmetry the #300 fieldtest
named (df_4). Registry gains list_process_ids/list_campaign_ids
(missing store dir reads as empty, matching the blueprint-scan
discipline); campaign_not_found_prose becomes the single source for
show_campaign's two not-found arms. The ambiguity arm is pinned by a
unit test at the resolution seam — hash-derived ids make a
shared-prefix E2E fixture impractical.
|
||
|
|
6b994cead3 |
test: red for show resolving a unique content-id prefix (refs #302)
aura {process|campaign} show is exact-match-only while every surface
prints 8-hex prefixes and reproduce has resolved prefixes since #298;
the fieldtest's prefix paste (df_4) hits the same flat not-found prose
as a genuinely-unknown id. The E2E test pins the wanted headline: a
unique 8-hex prefix of a registered id prints byte-identically what the
full-id show prints (#164 byte discipline), both verbs. The ambiguity
refusal is deliberately left to a unit test at the resolution seam —
hash-derived ids make a shared-prefix E2E fixture impractical.
|
||
|
|
a5ca0e7481 |
feat: the select-rule refusal enumerates the accepted roster (closes #301)
An unknown select rule in a process document now refuses with the accepted vocabulary beside the quoted offending value: block std::sweep: unknown select rule "plateau" (accepted: argmax | plateau:mean | plateau:worst) mirroring the roster-naming metric-refusal shape. The roster is derived from slot_kind_label(SlotKind::SelectRule) — CLI describe output and the document refusal stay in sync by construction — and a companion test pins the "select rule: " prefix that derivation trims, so a label-wording drift breaks a test instead of silently degrading the prose. No alias-hint sentence: the enumeration alone rescues the bare-plateau paste case (fieldtest df_3), and the document schema stays strict per #300 F7. |
||
|
|
edb8c37916 |
test: red for the select-rule refusal enumerating the accepted roster (refs #301)
A process document carrying an unknown select rule refuses correctly but names no accepted vocabulary; the fieldtest's bare-plateau paste (df_3) hits an opaque wall. The unit test pins the wanted property at the site that builds the prose (select_from): the refusal enumerates argmax, plateau:mean, plateau:worst beside the quoted offending value. No alias-hint sentence — the enumeration alone rescues the paste case and the project's refusal prose stays sparse; the document schema stays strict per #300 F7. |
||
|
|
0a70f67711 |
fix: register outside a project refuses instead of creating a stray store (closes #305)
register_process and register_campaign now carry the same env.provenance() no-project guard as their show/runs siblings, refusing with exit 1 and the missing-Aura.toml prose instead of letting Env::runs_root()'s relative fallback materialise ./runs/ in an innocent cwd. Five pre-existing register tests ran from a bare temp cwd and were reaching their asserted behaviour only through this bug; they now run inside the fresh_project() fixture, preserving their original property (content addressing, intrinsic-validation refusals) in a real project. Verified: the RED E2E test (register_outside_a_project_refuses_and_ leaves_no_store, both verbs, refusal + no side-effect store) observed GREEN by the loop's independent verify alongside the full suite. |
||
|
|
27ace153f2 |
test: red for register's stray-store creation outside a project (refs #305)
register_process/register_campaign call env.registry() without the no-project guard their show/runs siblings carry, so Env::runs_root()'s relative fallback silently creates ./runs/ in an innocent cwd and registers the document there (exit 0). The E2E test pins both verbs and both halves of the wanted refusal: exit 1 naming the missing Aura.toml, and no store directory created as a side effect. |
||
|
|
da37f596f2 |
fix: show outside a project names the missing Aura.toml, not the id
show_process/show_campaign now guard env.provenance().is_none() before
touching the registry — the guard their siblings (campaign_runs,
validate_campaign_file) already carry — refusing with the honest
degradation prose ('needs a project ... no Aura.toml found up from
{cwd}') instead of blaming the id with 'not found in the project
store' against a store that does not exist. Refusal exit 1 unchanged.
The register-verb sibling gap (silently creating a stray ./runs/) is
deliberately NOT touched here — tracked as #305.
RED-first: the in-process pin landed at 803c970; independent verify
re-ran it green with the suite.
closes #304
|
||
|
|
803c9704cc |
test: red for show's no-project refusal blaming the id (#300 fieldtest)
Outside any project, show_process/show_campaign print 'not found in the project store' — asserting a store that does not exist and blaming the id, where the sibling verbs (campaign_runs, validate) degrade honestly naming the missing Aura.toml. The in-process RED pins: the refusal stays Err, its prose names 'no Aura.toml found up from', and does NOT claim 'not found in the project store'. Diagnosed cause: both show fns lack the env.provenance().is_none() guard their siblings use, so the no-project fallback store's Ok(None) masquerades as an unregistered id. refs #304 |
||
|
|
63711a1d3a |
fieldtest: document-first — 4 examples, 7 findings
Downstream-author fieldtest of the #300 surface, public interface only, real GER40 archive. All four axes hold: the closed generate -> show -> hand-extend -> re-register -> run loop (2-cell extended campaign), the typed stop grammar's clap rejections, bare plateau's content-id identity with plateau:mean, and the show read-back (byte-identical, no framing) with a clean unknown-id refusal. Findings routed: 1 bug (show outside a project blames the id instead of the missing Aura.toml — RED-first fix follows), 2 friction items filed to the tracker (opaque process-document select refusal; no prefix-id hint on show), 4 working (carry-on). The fieldtest spec survives git-ignored as the next planning cycle's reference input. refs #300 |
||
|
|
a12617bb80 |
audit: document-first cycle close — doc verb rosters caught up
Architect review (cycle
|
||
|
|
757e3ac1bd |
feat: the document-first surface closes — show read-back, typed stops, bare plateau
The #300 keystone cycle: the residual run-quintet flags were already document vocabulary on the --real arm (#210/#220); this closes the loop and ratifies the surface. - `aura process|campaign show <content-id>` prints a registered document's canonical bytes (print!, no framing — #164), so generate -> retrieve -> hand-extend -> re-register needs no direct store filesystem access. Refusals reuse the store-hint prose, exit 1. - walkforward/mc --stop-length/--stop-k become clap-typed i64/f64 (generalize's form); stop_knob_or and its parse_csv_list helper are deleted (multi-value/invalid input is now clap's exit-2 rejection — the two dissolved-verb refusal tests retarget, the type-dead unit test is deleted). - --select accepts bare `plateau` as the documented default plateau:mean (#227 carry); document schema untouched — the two spellings generate content-id-identical campaigns (test-pinned). - family.rs's quadruplicated demo stop literal collapses onto one DEFAULT_STOP const (maintainer-lens drift finding). - Ledger: C25's "which projection next" line resolved (document-first delivered, per-verb identity re-ratified — F8; host/MCP stay demand-driven); C18 records the read-back. Design triage, fork decisions F1-F8, and the auto-signed spec live on the reference issue. Verification: full workspace suite 1482 passed / 0 failed with the real GER40 archive exercised (the byte-identity round-trips ran, not skipped); clippy -D warnings clean. closes #300 |
||
|
|
4ed6455b64 |
fix: load_family resolves the enumerated family name, refusing ambiguity (#298)
GREEN for 78e68e6's RED. aura_runner::reproduce::load_family resolved
only the derived C18 handle '{family}-{run}' while the registry's
enumeration exposes the bare name factor — the natural
list-then-reproduce library workflow dead-ended (found by the #295
fieldtest; predates the extraction byte-identically). Resolution now:
exact-handle match keeps first precedence; a bare name naming exactly
one stored run resolves as fallback; an ambiguous name (several runs,
or a name/handle collision) refuses without guessing, listing the
candidate handles. Two additional tests pin the ambiguity refusal and
the handle-precedence-on-collision semantics.
Ledger record-reality rider: the C18 #158 paragraph claimed reproduce
refuses an unknown id with exit 2; the code has always exited 1 —
correct per C14's partition (the id names missing recorded state, a
runtime failure, not a usage error). The paragraph now records the
actual behaviour and the new name-fallback semantics.
Verification: cargo test --workspace green (1477 passed, 0 failed);
clippy --workspace --all-targets -D warnings clean.
closes #298
|
||
|
|
78e68e6002 |
test: red for the enumerated family id rejected by load_family (#298)
Registry::load_family_members() exposes the bare name factor while
aura_runner::reproduce::load_family exact-matches only the derived C18
handle '{family}-{run}' — the natural list-then-reproduce workflow
dead-ends on its first attempt (found by the #295 fieldtest; predates
the extraction byte-identically). The test pins: the enumerated
identity of an unambiguous single-run family resolves through
load_family to the same family the canonical handle names.
refs #298
|
||
|
|
9df217d868 |
fieldtest: shell-boundary — 4 examples, 6 findings
Downstream-consumer corpus for the #295 library surface: a standalone workspace (path-deps, no aura-cli in the link graph — cargo tree verified) exercising the four cycle axes as a World program would, public interface only: a DefaultMemberRunner member run over real GER40, a hand-authored campaign document through aura_campaign::execute, the IC + registry deflation dispatch as library calls, and load_family/reproduce round-trips. Run transcripts committed beside the sources. Findings routed separately: the family-id mismatch between the registry enumeration and the reproduce keying (list-then-reproduce dead-ends) to debug; the reproduce-API friction (no report on the simple path; DataSource/pip re-specification the manifest already holds, wrong pip silently DIVERGED) to a follow-up. Four working confirmations, chief among them the cycle's acceptance claim: all four axes compiled first-try from the public docs alone. refs #295 |
||
|
|
5006766579 |
audit: shell-boundary cycle close — drift resolved in-commit
Cycle-close audit (architect: drift_found; design core clean). What holds, architect-confirmed with the guard run and the diff read in full: the shell boundary is real and enforced (full-workspace c28_layering incl. completeness + shell-content checks, acceptance grep clean — no member-run symbol left under crates/aura-cli/src); aura-campaign keeps zero production dependency on the runner; the IC move is verbatim against the anchor; the new library-only E2E pins C1 (two independent runners, byte-identical RunReport). Drift items, all resolved as fixes in this commit: - design ledger: the C28 status sentence "No crate exists yet for measurement" contradicted this cycle's own phase-3 done line — now records aura-measurement as seeded (#295), execution still unbuilt. - the #147 registry-dispatch acceptance tests (IC deflation null, seeded determinism, cross-vocabulary refusal) were pure library properties stranded in the shell's test module — relocated to aura-measurement/tests/registry_dispatch.rs (engine/registry as dev-only, layering-exempt edges), per the cycle's own tests-move- with-their-module rule. - aura-runner's direct production edge on the external data-server tree was structurally invisible (the direction table checks aura-* keys only) and unrecorded — the C28 assembly prose now names it, and a new c28_layering test pins the external tree's entry points to exactly {aura-ingest, aura-runner, aura-cli}. - the C28 shell prose named three buckets that did not cover two real shell residents — it now names the op-script construction front-end (translation) and the `aura new` scaffolder (authoring-tooling, shell-resident like rendering until a second consumer wants it). Deliberately deferred, tracker homes exist (not drift): #297 (process::exit sites in aura-runner's single-run verb paths), #294 (IC duplicate-timestamp semantics), #288-era rustdoc unresolved-link warnings in aura-std/aura-backtest (pre-existing byte-identically at the anchor). No regression scripts are configured (the bench is report-only); the architect review is the gate. Verification: cargo test --workspace green (1474 passed, 0 failed) incl. the relocated registry-dispatch tests and the new data-server guard; clippy --workspace --all-targets -D warnings clean. refs #295 |
||
|
|
170c6c82dc |
feat: full-workspace C28 guard — the shell boundary is structural (#295 part 2)
Completes the shell-boundary cycle (tasks 10-13): - c28_layering now enumerates the FULL workspace: every crate row is reconciled against its real production [dependencies], a completeness assertion pins the table to the on-disk crates/ set (a new crate can no longer escape the guard silently), the shell/assembly imported-by- nothing rule is asserted, and a shell-content check pins aura-cli to no-[lib] plus a closed allow-list of argv/translation/presentation modules. A new domain module in the shell now fails the suite. - Ledger amendments: C28 gains the assembly position (aura-runner) and the corrected import-rule prose (the ratified aura-campaign -> aura-backtest production edge, #291/#292); phase 3 is marked done with the #297 process::exit residual named; a provenance note records this as structural-debt closure, not a demonstrated downstream blocker. C25 records the control-surface decision: the text artifact vocabulary is canonical, every control surface (CLI executor verbs, a future host, an MCP face, a World program) is a projection/executor over it — the which-projection-next ranking deliberately open on #295. C14 gets the executor-face amendment, C26 the binding-module relocation. - aura_campaign::MemberRunner's doc names the shipped default implementor (aura-runner::DefaultMemberRunner) while the column keeps zero dependency on it. - New library-only E2E fixture (aura-runner/tests/world_member_run_e2e): runs the canonical member recipe over a tiny synthetic archive with no aura-cli in the link graph, and pins C1 determinism (two independently constructed runners produce a byte-identical RunReport). - campaign_run.rs module doc: intra-doc MemberRunner link re-anchored to aura_campaign::MemberRunner (the impl moved out with part 1). Verification: cargo test --workspace green (1473 passed, 0 failed); clippy --workspace --all-targets -D warnings clean; cargo doc clean of NEW warnings (the five unresolved-link warnings in aura-std/aura-backtest predate this cycle byte-identically at the anchor — #288-era doc drift, left for the cycle audit). refs #295 |
||
|
|
5c2ac982bc |
refactor: extract the member-run recipe into library crates (#295 part 1)
The shell no longer defines what an aura backtest is. Tasks 1-9 of the shell-boundary cycle — structural extraction, behaviour byte-identical: - aura-runner (new; the C28 assembly position): input binding (the C26 module, moved whole), the C1-load-bearing param<->config translators, harness assembly (wrap_r / run_signal_r / run_blueprint_member and the probe/reopen cluster), axis + risk-regime conventions (bind_axes et al.), the campaign family builders + MC guards, reproduce (process::exit -> RunnerError, shell remaps to identical bytes), the measurement-run orchestration, project loading (Env / cdylib load / charter / staleness, moved whole), the coverage gap-walk (deduplicated onto interior_gap_months), and DefaultMemberRunner — the public implementation of aura_campaign::MemberRunner (ex CliMemberRunner). The MemberRunner trait stays in aura-campaign; the column still imports no harness/data-binding machinery. - aura-measurement (new; seeds C28 rung 3): the IcMetrics/IcKey vocabulary + information_coefficient, verbatim incl. serde derives (#294 duplicate-timestamp semantics move as-is, unresolved). - aura-backtest: the pure per-run scaffold (point_from_params, wf_ms_sizes / fit_wf_ms_sizes, intersect_shared_window). - shell residue: main.rs / campaign_run.rs keep argv/dispatch, argv->document translation, and presentation only; walkforward_summary_json_from_reports now calls the public aura_engine::param_stability instead of its inline twin. - worked example (crates/aura-runner/examples/world_member_run.rs): a World program runs a member backtest through the library alone. Held quality findings, adjudicated: the plan's literal pub-visibility list for binding.rs kept (cosmetic); ~20 refusal sites inside aura-runner (family/member/measure/translate) still process::exit — behaviour-identical today, conversion to RunnerError is filed forward (refs #297); rustfmt line-width drift on re-pathed call sites left for a repo-wide fmt decision. Verification: cargo test --workspace green (1471 passed, 0 failed); cargo clippy --workspace --all-targets -D warnings clean. Byte-identity is pinned by the untouched shell E2E suites (cli_run, measure_ic, run_measurement, research_docs, run_refuses_unrunnable_blueprint, tap_recording — zero assertion edits). Remaining in this cycle: full-workspace c28_layering + shell-content check, ledger amendments (C28 assembly position, C25/C14 control-surface consequence, C26 realization note). refs #295 |
||
|
|
2cf4574e33 |
audit: cycle metric-vocabulary tidy — ledger records the retired #147 deferral
Cycle-close audit (architect: drift_found, design core clean). What holds, architect-confirmed: C18 bit-identity for RunMetrics (prose derived byte-identically, rng order preserved, registry suite unchanged); the C10 wall (monomorphic R-gates, r_based in the R vocabulary, cross-vocabulary refusal tested both ways — no leak); C28 direction (trait in aura-analysis, vocabulary supplied from the outer rungs, zero Cargo edge changes); C1 determinism pinned through the generic path. Drift items, all resolved as fixes in this commit: - design ledger: the C28 #147 disposition now records item 2 SHIPPED (the A1 cut) with A2 still deliberately deferred; the #136 one-implementor clause carries a supersession note (the IC is the second implementor). - seven stale doc comments describing the pre-#147 or mid-cycle state (analysis trait + estimator docs, registry check_r_metric C9 claim, engine re-export note, campaign PER_MEMBER_METRICS roster note, member-seam guard comment, research vocabulary note) updated to the shipped state. - R_BASED_METRICS is now oracle-pinned against RunMetricKey::r_based() in the vocabulary test (it feeds the NonRMetric refusal prose; a divergence would have misreported the R-gate silently). Noted, not amended (history stays): commit 6744f67's body says 'three syntax-only edits' where the test module actually took five. No regression scripts are configured (the bench is report-only); the architect review is the gate. Verification: extended vocabulary test, campaign suites, analysis/research suites green; clippy --workspace -D warnings clean. refs #147 |
||
|
|
0651e16146 |
feat: the IC becomes the second metric vocabulary; rosters single-sourced
The A1 cut of #147 item 2, part 2 of 2 (the consumers), completing what part 1 (the MetricVocabulary substrate) set up: - aura-campaign's RANKABLE_METRICS hand-copy becomes a re-export of the backtest vocabulary's roster; the #190 guard test extends to pin the roster nesting (rankable == vocabulary roster, rankable within per-member, per-member within the research vocabulary) and the legacy exposure_sign_flips alias, so any drift fails a test loudly. The research and per-member rosters themselves stay hand-copies by ratified isolation (#190 accepted residual) — now oracle-pinned instead of trusted. - aura-cli gains IcMetrics + IcKey implementing MetricVocabulary: the second PRODUCTION implementor. The standalone `aura measure ic` reduction now computes its permutation p via the shared null_draw + one_sided_p_laplace building blocks (independent draws instead of chained shuffles — statistically equivalent, deterministic per seed; emitted p values may differ in the last digits from #290's). - Acceptance proven at the registry boundary: an engineered SweepFamily<IcMetrics> deflates through optimize_deflated to a LOW overfit probability and a noise family to a HIGH one (the permutation null, dispatched by the vocabulary — the deflation machinery is no longer baked-in R-only); same seed reproduces the identical FamilySelection; cross-vocabulary names are refused with the family's own roster in the prose (the C10 guard). A new cli_run E2E exercises the legacy alias through the real `aura runs family … rank` boundary. Deviation from the plan, forced by the compiler: IcMetrics additionally derives Debug (unwrap_err on the deflation Result needs it; mirrors RunMetrics). Verification: full workspace suite green (no FAILED, no compile errors), clippy -D warnings clean, C18 goldens (cli_run, measure_ic, c28_layering) green unchanged. A2 — measurement runs as sweep-family citizens — stays deliberately deferred until a concrete campaign demand exists. closes #147 |
||
|
|
6744f670b1 |
refactor: registry deflation dispatch generic over the MetricVocabulary trait
The A1 cut of #147 item 2, part 1 of 2 (the substrate): the registry's ranking/deflation machinery no longer bakes in the R metric vocabulary. - aura-analysis gains the deliberately narrow MetricVocabulary trait (name resolution, roster, direction, value read, one null draw) and the single-sourced one_sided_p_laplace estimator; re-exported through aura-engine's existing seam (no Cargo.toml edge changes anywhere). - aura-backtest now OWNS the R vocabulary: RunMetricKey (was the registry's private Metric enum, exposure_sign_flips alias preserved), r_based (the C10 classification), R_BASED_METRICS/RANKABLE_METRICS rosters, the centred moving-block null_draw, and member_metric_from_rs. - aura-registry: metric_cmp/rank_by/null_best_of_k/member_sd generic over M: MetricVocabulary; optimize/optimize_plateau/optimize_deflated over M: MetricVocabulary + Clone (SweepPoint<M>: Clone bounds on M: Clone); the private vocabulary deleted; check_r_metric/generalization stay monomorphic on the RunMetrics vocabulary (the C10 wall by type); RegistryError::UnknownMetric carries the vocabulary's roster so the refusal prose derives instead of being hand-strung (byte-identical for R). Bit-identity (C18): rng consumption order, centring, Laplace arithmetic, floors, tie rules, and refusal prose are all unchanged for RunMetrics inputs — the full registry suite (75 tests) passes unchanged except three syntax-only edits for the UnknownMetric variant reshape; clippy -D warnings clean on the three touched crates. Alternatives considered: a vocabulary table in aura-core (rejected: inverts C28's supplied-from-outer-rungs direction and cannot host the null model); a parallel measurement-side deflation path (rejected: duplicates the machinery and adds a fifth roster). Part 2 lands the consumers (campaign roster single-sourcing, the IC implementor, acceptance tests). refs #147 |
||
|
|
a9d36ddd70 |
feat: measurement's first deflatable metric — the Information Coefficient
Give a measurement run a standalone post-run quality score: the Information
Coefficient (IC), corr(signal_t, forward_return_{t+h}), with a permutation
null model. Before this a measurement run persisted only tap names and series
(MeasurementReport) — inspectable but not rankable or deflatable. IC is the
first metric measurement supplies: the tap-to-scalar reduction the C28
process-column / metric-interface seam anticipates ("a named metric vocabulary
supplied by measurement and backtest instead of baked in R-only").
Surface: `aura measure ic <run> --signal <tap> --price <tap> [--horizon]
[--permutations] [--seed]`. It reads the run's two recorded tap traces, builds
the forward-return series offline over the recorded price spine, aligns
signal -> return by exact timestamp, computes the IC, and prints it with a
one-sided permutation-null overfit probability (Laplace-smoothed, mirroring the
R deflation arm's formula in optimize_deflated).
Placement follows the C28 ladder: the generic pieces — pearson_corr and a
Fisher-Yates permute over SplitMix64 — land in aura-analysis (the domain-free
statistics foundation, beside resample_block); the IC semantics (forward-return
horizon, the offline join) and the CLI verb land in aura-cli (the shell). The
run path (run_measurement), MeasurementReport, the trace store, and the registry
metric vocabulary are untouched — existing `aura run` output stays byte-identical
(the C18 golden run_prints_json_and_exits_zero stays green).
Causality (C2): the forward-return read close_{t+h} is over a completed run's
recorded trace, not an in-graph node. A look-ahead signal is structurally
impossible in a causal run, so the engineered-vs-noise acceptance property is
unit-tested over hand-built offline series (perfect signal -> IC ~= 1, p < 0.05;
a varying but exactly-uncorrelated signal -> IC = 0, not significant;
constant/degenerate -> the (0.0, 1.0) floor), while the E2E validates the CLI
wiring, report well-formedness, determinism, and the error paths over a real run.
This lands the second, structurally distinct null-model computation (permutation,
beside the moving-block bootstrap behind R). That makes the deferred registry
deflation-vocabulary abstraction (#147 item 2) demand-driven rather than
speculative — the next cycle.
Verified: cargo test --workspace green (incl. the new aura-analysis unit tests,
the aura-cli ic_* unit tests, and the measure_ic E2E); cargo clippy --workspace
--all-targets -D warnings clean; the C18 byte-identity golden unchanged.
closes #290
refs #147
|
||
|
|
a56ab7859d |
refactor: cut the engine's backtest-metrics edge via RunReport<M>
C28 phase 2 (Stratification); realizes item 1 of the deferred #147. The engine's production surface no longer names a backtest-metric type: - RunReport becomes generic over its metric payload M; sweep/mc/walkforward/ blueprint thread the parameter (SweepPoint<M>, SweepFamily<M>, WindowRun<M>, WalkForwardResult<M>). RunManifest stays concrete and engine-owned (its selection: Option<FamilySelection> embeds the foundation-grade analysis type). - summarize and the MC assembly (McDraw/McFamily/McAggregate/RBootstrap/ r_bootstrap/monte_carlo) move to aura-backtest - McAggregate::from_draws reads RunMetrics fields by name, so generifying it is the phase-6 metric-vocabulary abstraction (#147 item 2), still deferred; wholesale relocation is the honest cut. The concrete instantiation lives in aura-backtest as `type RunReport = aura_engine::RunReport<RunMetrics>` + sibling aliases. - the statistics kernel (MetricStats/quantile/resample_block/SplitMix64) moves to the aura-analysis foundation; the engine re-imports it (inner->foundation, legal) and re-exports it so existing consumers stay source-compatible. Dependency inversion in one commit: aura-engine drops aura-backtest from [dependencies] (back to dev-deps for its SimBroker/RunMetrics test fixtures); aura-backtest gains aura-engine. Cycle-free for lib targets - the cycle closes only through the engine's dev-dep edge, the pattern aura-vocabulary already uses. aura-backtest reaches the kernel transitively through the engine re-export, so no aura-backtest -> aura-analysis edge exists (the C28 ladder permits backtest -> {core, engine} only). run_indexed / SplitMix64::next_f64 widened pub(crate) -> pub for cross-crate use. Consumers (registry/campaign/cli/composites/ingest/bench) rewired by import path only, no call-site logic changed. The c28_layering structural test extends to the full ladder: aura-analysis (no aura-* deps), aura-engine ⊆ {core, analysis}, aura-backtest ⊆ {core, engine}. Behaviour-preserving: 1448/0 tests, clippy -D warnings clean, serde shapes byte-identical (C18 - RunReport<M> keeps field order manifest,metrics; the CLI pre-serialized-splice contract unchanged), moved code traceable via git rename detection. Cycle-introduced broken intra-doc links fixed. closes #292 |
||
|
|
94aaa4cde8 |
refactor: split aura-analysis into statistics and backtest metrics
C28 phase 5 (Stratification): the backtest reductions - RunMetrics, RMetrics, summarize_r, r_metrics_from_rs, and the position-event table - move verbatim into aura-backtest::metrics, beside the position_management producer whose record layout they read (the r_col/cost_col lockstep contract is now intra-crate; its stale "aura-std" comment corrected). aura-analysis is reduced to the domain-free half: the multiple-comparison hurdle math (inv_norm_cdf, expected_max_of_normals) and the selection-provenance types (FamilySelection/SelectionMode - kept beside the statistics so RunManifest.selection embeds a foundation-grade type), [dependencies] = serde only. The engine re-export surface is name-unchanged (report.rs re-imports from both crates), so no indirect consumer needed a source edit; aura-backtest moves from aura-engine's dev-dependencies into [dependencies] as a commented TRANSIENT widening of the C28 violation, removed by the phase-2 edge cut on this branch. The campaign drift guard imports its pinned types from their new source crates. Behaviour-preserving: 1448/0 tests, clippy -D warnings clean, serde shapes byte-identical (C18), moved code traceable via git copy detection. closes #291 |
||
|
|
b39fd63396 |
refactor: split the aura-std roster into C28 layer crates
Phase 4 of the Stratification milestone. aura-std held four C28 ladder layers in one roster; this cuts them into layer-aligned, aura-core-only node crates so the import direction is enforced by the crate graph: - aura-std — engine nodes only (arithmetic/logic/rolling + sinks) - aura-market — session, resample - aura-strategy — bias, stops, sizer, cost-model machinery - aura-backtest — sim_broker, position_management - aura-vocabulary — the relocated closed std_vocabulary roster Node modules move verbatim (byte-identical renames); consumers are rewired by import path only. A new structural test (aura-vocabulary/tests/c28_layering.rs) asserts each node crate's [dependencies] stay within its C28-permitted inner set, catching the acyclic-but-outward violation the compiler misses. Behaviour byte-identical: full workspace suite green (1448 tests), no golden edited, clippy -D warnings clean. C28 Status block updated. closes #288 |
||
|
|
34ff539143 |
feat: measurement run verb — shape-dispatch aura run on blueprint output
Phase 3 of the Stratification milestone (contract C28). `aura run` now
shape-dispatches on the loaded blueprint: a `bias` output takes the
existing strategy path (wrap_r + R evaluation), byte-identical; a
blueprint that declares taps but exposes no `bias` runs bare via the new
`run_measurement` (bootstrap, run, drain the declared taps) with no
SimBroker, no risk executor, and no per-cycle equity/exposure/r
recorders; a blueprint exposing neither is refused. This gives the
measurement layer its own run verb and removes the O(cycles) recording
retention (the measured ~2 GB peak RSS over 12y UK100 M1) the mandatory
R scaffold imposed on a measurement-shaped run.
A new `MeasurementReport { manifest, taps }` (aura-engine::report, beside
RunReport) is the stdout/record shape — no R metrics, since a bare run
has no broker. The manifest's `broker` carries an interim "measurement"
sentinel; the honest `Option<String>` model is deferred to the #147
metric-genericity block, keeping this phase #147-free and the
strategy-path C18 goldens byte-identical.
`run_signal_r` is untouched (the tap machinery is duplicated into
`run_measurement`, not extracted). The measurement arm's closed-blueprint
guard uses `signal.param_space()` rather than `blueprint_axis_probe`,
which welds wrap_r and would panic on a no-`bias` signal.
refs #286
|