14c43474ab01224ab22aef39bf6813c4578a06e7
963 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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
|
||
|
|
1c49d5dce2 |
design: ratify C28 — internal stratification (ladder, process column, shell)
Codify the layer model settled with the user: an inner-to-outer ladder
engine -> market -> {measurement | strategy} -> backtest -> execution,
the research-process column beside the ladder, the shell outside. States
the import rule (inner never imports outer; measurement/strategy
siblings; column imported by no ladder crate; shell imports all;
dev-deps exempt), the six seams (five already ratified contracts —
C8/C24/C27/C18/C10-C13 — only the metric interface new), and an honest
Status recording the one hard production violation (engine -> analysis
via report.rs re-exports + the R-typed mc.rs fields) and its coupling to
the deferred #147.
Also restore the "### C19" header, deleted as collateral of
|
||
|
|
592750b122 |
audit: close #284/#285 cycle — GraphBuilder::tap twin restores C24 symmetry
Cycle-close tidy for the #284 (tap construction op) + #285 (docs) cycle. The architect drift review (cb6211c..HEAD) came back drift-clean on the ledger contracts the cycle touched — C27 machinery untouched, a faithful `expose` twin (C25 preserved), lockstep op<->OpDoc / OpError<->format_op_error arms paired, and the #285 docs verified accurate against aura-std/src/session.rs — save one drift item, fixed here. Drift item (fixed): the op-script gained `Op::Tap` but `GraphBuilder` had no `tap()` twin, softening C24 (replay-equals-builder = one construction semantics) and C17 (builder-primacy) — every other op verb has a builder twin. Unlike the op-script's `doc` gap (a ledger-recorded #125 scope cut), this asymmetry had no rationale; a tap is a first-class authoring act on both surfaces, so denying only the fluent builder was an accident of incremental delivery, not a design cut. Fix: `GraphBuilder::tap` (builder.rs), the exact output-side mirror of `expose` — a `taps: Vec<(String, OutPort)>` accumulator resolved at `build()` into `Tap { name, from: TapWire }` and threaded via `.with_taps`. Plus a construction.rs lockstep test `tap_replay_matches_builder` proving the op-script `Op::Tap` replay and the `GraphBuilder::tap` twin serialize to byte-identical blueprint JSON and compile to identical `FlatGraph.taps` — the tap op is the by-identifier face of the same construction semantics, not a second one. Carry-on (architect debt-low, no action): commit 43a1cc4's body phrase "op-built composites silently lost their taps" over-claims a prior drop (no tap op existed pre-cycle); the "latent" qualifier keeps it honest, so the wording is left as-is rather than rewriting an already-landed commit. Verification: full `cargo test --workspace` green; `cargo clippy --workspace --all-targets -- -D warnings` clean. The bench is report-only by project convention and was not run — this cycle adds a construction-time op + docs, no hot-path change. Fieldtest: the #284 `tap` surface is exercised from the public interface by two E2E consumer-path tests (op-script -> `aura graph build` -> `aura run` -> persisted trace) plus two worked authoring examples hand-verified through the built binary; #285 is documentation. The per-cycle fieldtest is satisfied. Spec + plan working files (docs/specs/tap-construction-op.md, docs/plans/tap-construction-op.md) discarded per audit Step 5 (git-ignored, never committed; their design intent lives in the commit bodies + the ledger). |
||
|
|
a5e02ff541 |
docs: SessionFrankfurt output semantics + declared-tap authoring (#285)
Surfaced by the measurement-milestone fieldtest: a downstream author reaching for session anchoring or declared taps had no public statement of either — the semantics lived only in node source / the design ledger. SessionFrankfurt / Session output semantics: - glossary `session node`: corrected — it claimed three streams (`bars_since_open`, `in_session`, `session_open_ts`), but the shipped node emits ONE `i64` field `bars_since_open`. Now states the real contract: the count of completed bar-periods since the local (tz-aware, DST-correct) session open, close-instant indexed (09:15->1, 09:45->3; pre-open <=0), with the Frankfurt 09:00 Europe/Berlin open + `period_minutes` knob + the value-ignored `trigger`. - authoring-guide: a "Session anchoring: the SessionFrankfurt preset" subsection — schema (trigger / period_minutes / bars_since_open), the close-instant multiples, the EqConst gating pattern, DST handling, and the add-snippet. Verified against `aura graph introspect --node SessionFrankfurt`. Declared-tap authoring (C27 / the #284 tap op): - authoring-guide: the `tap` op-table row (seven ops -> eight) + a worked "declaring a measurement tap" example. The example is verified: piping it through `aura graph build` emits `taps:[{"name":"spread","from":{"node":2,"field":0}}]`. - design ledger op-list (INDEX.md): a `tap` bullet (seven verbs -> eight); also corrected the `expose` "only verb that keeps `as`" claim — `tap` keeps it too. - README op-kind enumeration: adds `tap`. Docs only; no code change. Both worked examples were run through the built `aura` binary; the SessionFrankfurt schema was checked against introspect. closes #285 |
||
|
|
43a1cc44d3 |
feat(graph): name-addressed tap construction op — declared taps without raw indices
Declared taps (C27, #282) were authorable only by hand-patching the raw blueprint envelope's `taps` array, where `node` is a raw interior index — an index miscount was the expected mistake and the root friction behind the two `aura run` panics the measurement-milestone fieldtest found. Add a `tap` op to the construction op-script vocabulary — the exact output-side twin of `expose`. It names its source wire by the dotted `"node.field"` identifier the op surface uses everywhere (`split_port` + `resolve_output_field` + `node_index`), so `{"op":"tap","from":"fast.value","as":"fast_ma"}` replaces patching `{"node":14,"field":0}`. Engine (construction.rs): `Op::Tap`, its `tap()` handler (a verbatim twin of `expose` — same resolvers, same name-dedup, appending a `Tap` to a new `taps`/`tap_names` namespace), and `OpError::DuplicateTap` (the twin of `DuplicateOutput`, stricter than the raw envelope, which enforces no tap-name uniqueness). `finish()` now threads `.with_taps(self.taps)` into the built `Composite` — fixing a latent drop #282 left (it built `.with_gangs` but never `.with_taps`, so op-built composites silently lost their taps). CLI (graph_construct.rs): `OpDoc::Tap` (externally tagged, `"as"`-renamed like `expose`) + the `kind_label` / `From` / `format_op_error` arms (the last compiler-enumerated by the exhaustive match). No change to the `Tap` / `FlatTap` / `bind_tap` / `aura run` machinery — the op-declared tap flows through the existing #282 compile + record path unchanged. Verification: 7 new tests green (4 engine: resolve+append, bad-port / ghost-node refusal, duplicate-name refusal, finish-threads-into-FlatGraph; 1 CLI serde round-trip; 2 CLI E2E over the real `aura graph build` / `aura run` subprocess — one pinning the exact serialized `"taps":[{"name":"fast_ma","from":{"node":0,"field":0}}]`, one proving an op-authored tap round-trips through build -> run -> persisted trace). Full workspace suite green; clippy clean. The RED-before-GREEN step was verified by a temporary stash of the production edits (the `Tap` / `with_taps` machinery pre-existed from #282, so a fresh RED-only test could not be authored) — the new tests fail without the edits, pass with them. Docs (the authoring-guide `tap` row, the ledger op-list `tap` line, and a worked tap-authoring example) are deliberately out of this commit's scope — they land in #285, the docs follow-up in this same cycle. closes #284 |
||
|
|
cb6211c888 |
fix(cli): aura run refuses an unrunnable blueprint cleanly, not by panic (#282)
Two user-authorable blueprint shapes crashed `aura run` with an internal .expect instead of the CLI's clean aura: refusal register: - a tap wire pointing out of range: run_signal_r's compile now handles CompileError (TapWireOutOfRange) as an aura: refusal + exit 1, naming that the blueprint does not compile to a runnable harness. - an exposed output not named bias: a pre-check in dispatch_run (the single loaded-blueprint call site, before the free-knob axis probe that welds the hardcoded bias port and would itself panic) refuses with a message telling the user aura run expects a strategy with a bias output and listing what the blueprint exposes. The sweep path (run_blueprint_member) is untouched. Surfaced by the measurement-milestone fieldtest as the sharp edge of raw-index tap authoring (#284 tracks the name-addressed tap op that removes the miscount class at the source). closes #282 |
||
|
|
e768072ed0 |
test: red for aura-run panics on an unrunnable blueprint (#282)
Two RED tests pinning that `aura run` refuses cleanly (exit 1 + an aura: message) rather than panicking on a user-authorable blueprint: a tap wire pointing out of range (run_signal_r's compile .expect swallows TapWireOutOfRange into a panic, main.rs:1753) and an exposed output not named bias (wrap_r's g.build().expect panics on UnknownOutPort from the hardcoded bias weld, main.rs:1667). Surfaced by the measurement-milestone fieldtest. refs #282 |
||
|
|
9005fd53a2 |
docs(glossary): record the C27 author-declared sense of 'tap' (#282)
The measurement milestone overloaded 'tap': beside the closed campaign persist_taps vocabulary (equity | exposure | r_equity | net_r_equity), the word now also names a declared tap — a Composite.taps entry a hand-authored blueprint declares on an interior output wire (C27), the output-side twin of an input_role, bound run-mode-aware. Record-reality glossary update reconciling the two senses; a worked authoring example is owed at docwriter (#285). |
||
|
|
ba46350da9 |
fieldtest: measurement milestone — 3 studies, 8 findings
Milestone acceptance corpus for 'Measurement as a first-class citizen', authored as a downstream consumer (public interface only): a gated rate/mux/counter over the four new cells, a SessionFrankfurt anchor study, and the origin first-candle-momentum study on real GER40 data — each a hand-authored blueprint plus one recorded run, zero throwaway Rust. The three capabilities compose end-to-end and record via declared taps. Findings routed separately: two aura-run panics-where-refusal-expected (bad tap wire; non-bias output) to debug; a tap-authoring friction and two doc gaps to follow-ups. |
||
|
|
cf670caf46 |
audit: cycle-close tidy for the measurement milestone (#280/#281/#282)
Architect drift review over 16ca4e1..HEAD: drift-clean but for one low citation slip, now fixed here — snap_to_nearest_minute's doc cited the record-then-replay boundary as C6 (firing policy); it is C11. What holds (architect-confirmed): C27 taps match the contract (pure declaration, compile-resolve+hoist through the shared lowering remap, caller-built aura-core-only sink, unbound-is-inert, Tier-1 additive serde); the four #281 cells are C8/C6-clean (<=1 record/eval, warm-up None, When's quiet-None is the Resample precedent, barrier stall correct); #280's snap respects C11 (node-local reads only, firing/barrier timestamps untouched, stream verbatim); FlatGraph and validate_wiring lockstep complete. Regression (report-only bench): exit 0, all five surfaces fingerprint OK, deltas within noise on a loaded box — no baseline update, nothing to ratify. Scaling follow-up filed as #283: the single-run tap drain buffers O(cycles) rows (the #138 profile) — fine for the bounded acceptance run, to be folded before a full-history declared-tap study. |
||
|
|
7a4e5eb99d |
docs(ledger): C27 — declared taps contract (#282)
Records the tap/binding contract: taps are the output-side twin of input_roles (C26), resolved and hoisted at compile, bound run-mode-aware via a caller-built sink (engine stays aura-core-only), recorded on the single run and inert in a sweep. Documents the deliberate DCE-deferral — build-time elision now, chain-pruning when DCE (C23) lands — and the unbound-is-inert non-error asymmetry vs a mandatory input role. refs #282 |
||
|
|
2e8d74903c |
feat(cli): record declared taps on the single-run path (#282)
The payoff: a hand-authored blueprint's declared taps become recorded output from one `aura run`. run_signal_r binds each tap (hoisted into flat.taps after wrap_r nests the signal) to a fresh Recorder before bootstrap, drains the series after the run, and persists each as a ColumnarTrace via the existing env.trace_store() — the same trace surface the campaign path feeds, so the existing chart tooling reads them. Duplicate tap names are refused (the caller-owned DuplicateBind dedup) before anything persists. A tap-free run writes no trace store — byte-identical to today. Sweep/reduce leaves taps unbound (run_blueprint_member untouched). Covered end-to-end: a persisted tap series, the tap-free no-write guard, and the duplicate-name refusal. refs #282 |
||
|
|
93d0ec45f2 |
feat(engine): FlatGraph::bind_tap + TapBindError (#282)
The run-mode-aware bind seam. bind_tap attaches a CALLER-BUILT Box<dyn Node> sink to a declared tap — appending the sink node + an edge from the tap's (node, field) to its input, before bootstrap so the Kahn sort orders it. The engine constructs no aura-std type (it takes a Box<dyn Node>), keeping its production dependency aura-core-only. bind_tap raises UndeclaredTap; duplicate detection across binds is the caller's (the engine keeps no cross-call state), mirroring how bind_sources' DuplicateFeed is a supply-level fault. TapBindError is exported for the CLI. A bound tap records exactly the producer's per-cycle output through the full public pipeline; an undeclared name is refused without mutating the graph. refs #282 |
||
|
|
f3daded514 |
feat(engine): compile resolves and hoists declared taps (#282)
The compile core. resolve_tap_wire resolves a blueprint Tap's interior
{node, field} through the lowering table exactly as OutField re-exports and
edges resolve — a leaf to its remapped index (output-arity checked), a nested
composite through its output remap. Taps are terminal (not re-exported through
the boundary): a flat_taps accumulator threads through the lowering recursion,
so an interior composite's taps hoist to the root FlatGraph.taps with remapped
wires — the case the CLI single-run wrapper relies on. validate_wiring gains a
tap leg (threaded at all three call sites incl. GraphSession::finish) and
CompileError::TapWireOutOfRange names a bad wire. Root-resolve, out-of-arity,
and nested-hoist are pinned green.
refs #282
|