#164 — tighten the C24 "canonical, versioned" blueprint contract on two corners
the milestone fieldtest surfaced. Both forks were derivable; resolved with
rationale (recorded on #164).
Fork 1 — canonical artifact carries no trailing newline. `blueprint_to_json`
returns the JSON value with no trailing newline (647 bytes for the SMA-cross
fixture); `aura graph build` had framed it with a `println!` (648). The library
serializer is the single canonical source — the form content-addressed topology
identity (#158) hashes — so the CLI is a transport that must not mutate the
bytes: `build_cmd` now `print!`s the canonical bytes verbatim, making the CLI and
library emit paths byte-identical. RED-first: the new E2E
`graph_build_emit_is_byte_canonical_no_trailing_newline` pinned the trailing
newline (FAIL), green after the one-line change. The two cycle-0088 `.out.json`
goldens are re-recorded at 647 bytes. The loader stays lenient (a trailing
newline on input still parses, Tier-1 robustness; the milestone fieldtest's
`mt_4` tolerance check is unaffected).
Fork 2 — the canonical JSON is the blueprint equality/identity surface; no
second in-memory PartialEq/Debug is added. `Composite`/`BlueprintNode` cannot
derive them: `PrimitiveBuilder` (aura-core node.rs) holds a
`build: Box<dyn Fn(&[Cell]) -> Box<dyn Node>>` closure, neither comparable nor
printable. Equality could only be defined over the serialized data, of which
`blueprint_to_json` is already the single source; a separate in-memory equality
notion would be a redundant second source of truth and a drift hazard against
the form #158 hashes. Recorded the decision (discharges acceptance box 2's "or
JSON-string is the intended equality surface" arm) in the C24 ledger; no code
added for it.
Verified: cargo test --workspace green (51 suites, incl. the new RED->green E2E);
cargo clippy --workspace --all-targets -D warnings clean.
closes#164
#163 — the construction op-script (`aura graph build`) had no durable public
schema doc, and the only worked-example corpus had drifted out of sync with the
current binary, so a consumer copying the visible example was misled.
Grammar reference (durable): a compact op-script grammar — the six verbs
(source/input/add/feed/connect/expose), their keys, the typed-Scalar bind form
({"I64":2}) and the capitalized ScalarKind, the by-identifier / dotted-port
model, and the add-`name` vs expose-`as` distinction — added to the C24 ledger
entry, with a correct worked-example pointer. Surfacing the same grammar in
`aura graph build --help` is left to ride with the CLI-discoverability work
(#159), keeping this change decoupled from the deferred World/project-as-crate
layer.
Corpus refresh: the cycle-0088 fixture corpus replayed against the current
binary failed on two accumulated drifts. (1) `add`'s naming key was renamed
`as` -> `name` (3c4b667; `expose` keeps `as` as a real alias) — the inputs now
use `name` for `add` ops only. (2) The dataflow-cycle bug the 0088 fieldtest
found was fixed (1652042, closes#161), so `c0088_3m_two_cycle` is now a
rejection fixture: the gap-era cyclic-blueprint golden is removed and
`c0088_3m.err` carries the eager cycle-rejection message. FINDINGS.md gets a
dated refresh stamp; its findings are preserved as written at fieldtest time.
Verified: the four golden-bearing success fixtures (_1, _2_completed,
_2_partial via --unwired) reproduce byte-identically; all 15 error fixtures
produce the intended by-identifier `op N (kind): cause` (or finalize) message.
Doc + fixtures only, no engine/CLI code touched.
closes#163
Closes the cycle-0090 (#156) work and delivers the "Topology-as-data:
blueprint serialization & loader (C24)" milestone.
Cycle-0090 audit (architect drift review, range 7ad7f58..HEAD): drift-clean
on substance. Tier-1 forward-tolerance is genuinely serde-default
(deny_unknown_fields = 0 in every production struct), pinned at envelope +
primitive across both the in-crate and public-seam tests; Tier-2 refusals
real and green; no struct/signature/runtime change; the invariant-5
acyclicity lockstep (construction gate <-> bootstrap Kahn sort) undisturbed;
invariant-9 resolver stays injected, no registry. The one medium item the
architect flagged — the C24 Remaining block carried no deferral signal for
#158/#159 — is fixed in this commit's ledger update.
Milestone fieldtest (fieldtests/milestone-topology-as-data/, the close-gate
evidence): GREEN, 0 behavioural bugs. A downstream consumer ran the full
author -> serialize -> load -> construct -> introspect -> reproduce story from
the public surface alone — round-trip bit-exact on both authoring surfaces
(Rust builder + op-script CLI), build-free introspection, Tier-1/Tier-2
forward-compat by name, fail-fast diagnostics by identifier. Verified the
headline claims myself (the two Rust binaries + the CLI build/diff + the
fail-fast exits), not just the agent's report.
Findings triaged to the forward queue (none blocking): op-script grammar
undocumented + a stale cycle-0088 example (#163); canonical trailing-newline
divergence + Composite Debug/PartialEq ergonomics (#164); CLI discoverability
of build/introspect + no `graph run` (commented on #159); the newline as a
content-addressing prerequisite (commented on #158).
#158 (content-address topology in the manifest) and #159 (retire the
hard-wired harnesses) were moved OUT of this milestone: both are premature
until the World generates runs from blueprint-data (commit still fully
identifies every hard-wired topology), so they belong to the
project-as-crate / World cycle. The C24 ledger Status records the delivery
and the deferral.
Ephemeral cycle-0090 spec + plan removed per the lifecycle convention.
The Gitea milestone container is closed on user ratification (this run
tees up the close, does not click it).
The blueprint data format (C24) extends additively under one codified
two-tier discipline:
- Tier-1 (additive-optional): a new optional field/section is tolerated by an
older reader (serde ignores unknown fields — no deny_unknown_fields), with no
format_version bump; by C1 a new optional field defaults to prior behaviour,
so an old blueprint reproduces the same graph.
- Tier-2 (must-understand: a new node type, edge semantics, or structural-axis
kind): bumps format_version, so an old reader refuses cleanly
(LoadError::UnsupportedVersion / UnknownNodeType) rather than silently
building a different graph.
Most of #156 already shipped with #155 (cycle 0087): the envelope, omit-defaults
canonical form, and both Tier-2 load-path refusals were present and green. This
cycle pins the one previously-unproven property — Tier-1 forward-tolerance of an
unknown optional field on the current loader — and codifies the discipline.
Changes:
- New characterization pin (in-crate, blueprint_serde.rs):
unknown_optional_field_is_tolerated_byte_identically — an unknown optional key
at doc and primitive level loads byte-identically and runs bit-identically.
- Two public-seam E2E pins (blueprint_serde_e2e.rs, added by the E2E phase):
unknown_envelope_field_tolerated_through_public_api and
unknown_primitive_field_tolerated_through_public_api — split the two distinct
tolerance surfaces (envelope vs primitive) across the World/cdylib crate
boundary, where a deny_unknown_fields added to one would break forward-compat
while leaving the in-crate test green.
- LoadError::UnsupportedVersion doc reworded from the deferred "is #156" note to
the settled two-tier discipline statement.
- C24 ledger (docs/design/INDEX.md): Status codification sentence added; #156
dropped from Remaining (#158/#159 stay).
Derived fork (recorded on #156): the per-section required-flag mechanism (PNG
critical-vs-ancillary) is NOT built — the version envelope already gives the
must-understand refusal at version granularity, no current Tier-2 section
validates a finer scheme, and C1 holds either way.
The pin is a characterization test (GREEN on first run) — it documents existing
serde silent-ignore behaviour, no production code changed. Verified: cargo test
-p aura-engine green (all targets, three new tests confirmed by name); cargo
clippy --workspace --all-targets -- -D warnings clean.
Single-task iteration for #156: append the Tier-1 forward-tolerance
characterization test (unknown_optional_field_is_tolerated_byte_identically),
reword the LoadError::UnsupportedVersion doc to the settled two-tier
discipline, and codify it in the C24 ledger (Status sentence + drop #156 from
Remaining). No struct/signature/runtime change. refs #156
Iteration within the C24 topology-as-data milestone (#156). Codifies the
blueprint format's additive forward-compatibility under one discipline:
Tier-1 (additive-optional) = serde-default silent-ignore, no version bump,
C1 default-equals-prior-behaviour; Tier-2 (must-understand: new node type,
edge semantics, structural-axis kind) = bump format_version so an old reader
refuses cleanly rather than building a different graph.
Most of #156 already shipped with #155 (cycle 0087): the format_version
envelope, omit-defaults canonical form, and both Tier-2 load-path refusals
(LoadError::UnsupportedVersion / UnknownNodeType) are present and green.
The genuine remainder is one unproven property — forward-tolerance of an
unknown optional field on the current loader — plus codifying the discipline
in the LoadError doc and the C24 ledger entry. Not a new mechanism.
Derived fork recorded on #156: the per-section required-flag mechanism (PNG
critical-vs-ancillary) is NOT built — the version envelope already gives the
must-understand refusal at version granularity, there is no current Tier-2
section to validate a finer scheme against, and C1 holds either way.
Boss-signed on a grounding-check PASS (all current-behaviour claims ratified
by named green tests; the new Tier-1 pin correctly absent — this cycle's
deliverable). refs #156
Cycle-close audit for the /boss run that followed up the cycle-0088 construction
fieldtest (#161 cycle-reject + #162 diagnostics-by-identifier). Regression gate:
full workspace suite green (0 failed).
Architect drift review (range 86841b0..HEAD): drift_found, two items, both
documentation-side — the code is correct and green.
- What holds: invariant 5 / C9 acyclicity (closes_cycle is a correct eager
reachability gate, no false positive on a valid DAG); C24 no-second-validator
(blueprint.rs untouched, finish() only translates the gates' returned
CompileError); fieldtest corpus handled per convention.
- Resolved (fix path, ledger): the C24 Status now enumerates the #161 eager
acyclicity gate and captures the LOCKSTEP the architect flagged — closes_cycle
(construction-time) and the bootstrap Kahn sort (harness.rs, BootstrapError::Cycle)
are now two homes of invariant-5 that must co-evolve when the explicit
delay/register node lands; plus a note that the holistic finalize faults now read
by-identifier (#162) while still calling the unchanged gates.
Ephemeral 0089 spec + plan git-rm'd per the active-cycle artefact lifecycle; the
durable rationale is lifted to the ledger above. No code change in this commit.
Two tasks: item 1 (by-identifier finalize) lands atomically across both crates —
three OpError variants + finish() translation + the CLI presenter arms + five test
touches (two of them E2E twins plan-recon found that the spec's testing strategy
under-enumerated: construction_e2e.rs and tests/graph_construct.rs); items 2 & 3
(bind-mismatch prose + discoverable bind form) are additive CLI changes.
Three presentation-only defects the cycle-0088 fieldtest found on the
construction op-script surface: the holistic finalize fault leaks raw machine
indices (UnconnectedPort { node: 2, slot: 1 }) where the by-identifier mapping
demonstrably exists; the bind-kind mismatch leaks a Debug struct; the typed-Scalar
bind form is invisible from introspect --node. Boss-signed on a grounding-check
PASS (every load-bearing assumption ratified by a currently-green test or the
defining code).
Architect drift review of cycle 0088 (#157, the introspectable fail-fast
construction service: §A shared gate predicates, §B the GraphSession/Op/replay
per-op surface + introspection, §C the aura graph build/introspect CLI).
What holds (confirmed against the diff): no-second-validator (C24) — edge_kind_check
is the single kind-check site called by both validate_wiring and
GraphSession::connect, and validate_wiring / check_param_namespace_injective are
reused verbatim in finish(); engine domain-free (invariant 9) — construction takes an
injected Fn(&str)->Option<PrimitiveBuilder>, no registry, the closed match +
std_vocabulary_types live in aura-std; topology-as-data / no DSL (C24/C17/inv 10) —
the engine Op is serde-free, the OpDoc wire DTO is CLI-side, ops are static and
non-Turing-complete; replay-equals-builder byte-identity (construction_e2e.rs) protects
C9/C19/C23.
Resolution:
- fix (drift-med): the root-role-boundness gate was the one holistic check finish()
re-implemented instead of sharing — extracted check_root_roles_bound (blueprint.rs)
and called from BOTH compile_with_cells and finish(), so finish() now reuses ALL the
holistic gates and the no-second-validator claim is literally true. Behaviour-
preserving (same UnboundRootRole variant; unbound_root_role_is_rejected stays green).
- fix (drift-med): removed the #![allow(dead_code)] on construction.rs — empirically
unnecessary (the pub re-exported surface is reachable; clippy -D warnings green
without it) — and its now-inaccurate comment (it claimed removal "when the consumer
lands", but the consumer is a separate crate). Also tidied a stale graph_construct.rs
module-doc line.
- fix (ledger): C24 Status refreshed — #157 moved out of "Remaining" with a cycle-0088
realization note (the construction service: eager/holistic gate split, build-free
introspection, emits the #155 blueprint); #159 reframed as paired with #157.
- carry-on (debt-med, forward-noted): the std_vocabulary roster is now a third hand-kept
copy (match + test list + std_vocabulary_types) and introspect --vocabulary escalates
#160's gap to user-facing — noted on #160; still fails safe.
- carry-on (scaling-low, forward-noted): the engine Op -> wire OpDoc direction is
unguarded for future Op variants — noted on #156 (the format-extension track).
No architect-sweep / regression script is declared in the project facts, so the suite
is the gate: cargo test --workspace 51 suites green; clippy --all-targets -D warnings
clean. Cycle drift-clean.
Ephemeral cycle artifacts removed (lifecycle convention): docs/specs/0088-*,
docs/plans/0088-*.
closes#157
Iteration-2 tasks (9-12) appended to the cycle-0088 plan: the JSON op-list serde
DTO (CLI-side, engine Op stays serde-free), `aura graph build` (replay -> emit
#155 / fail fast at op N), `aura graph introspect` (--vocabulary / --node /
--unwired), and an E2E suite driving the aura binary. All changes in aura-cli;
the engine core (ea1ca32 + 27ac4dc) is consumed unchanged. Wire-format forks
derived + recorded on #157.
refs #157
Iteration-1 §A of the construction service (#157): the surface-agnostic shared
predicates the eager op-script path and the holistic finalize gates both call —
no second validator (C24).
- edge_kind_check (blueprint.rs): the per-edge producer/consumer kind check,
lifted verbatim out of validate_wiring's edge loop into a pub(crate) fn that
validate_wiring now calls — behaviour-preserving (same Bootstrap(KindMismatch)
variant; the existing compile-time gate test stays green).
- resolve_input_slot / resolve_output_field (builder.rs): the exactly-one-match
name→index resolution extracted as pub(crate) fns over (&NodeSchema, &str), so
the runtime-String op-script names share GraphBuilder's resolution; GraphBuilder
delegates and maps to the unchanged BuildError variants.
- try_bind + BindOpError (aura-core node.rs): the fallible Result twin of bind
(bind keeps its panic contract and pinned messages verbatim; try_bind is a
separate method reporting the same three conditions as values).
- check_param_namespace_injective + validate_wiring widened to pub(crate) for the
finalize-stage reuse by the upcoming GraphSession::finish (§B).
Partial iteration: §B (the GraphSession/Op/replay surface + introspection) and
the §A→§B integration land next (plan Tasks 4-8). compile_with_params /
check_ports_connected are behaviourally unchanged. Full suite green; clippy clean.
Plan correction folded in: Task 3's test originally referenced aura_std::Sma —
infeasible inside aura-core (aura-std depends on aura-core; the reverse edge is a
dependency cycle). Adapted to a local PrimitiveBuilder probe with identical
assertions.
refs #157
Iteration-1 plan for #157 §A+§B: the eager/holistic gate split (extract
edge_kind_check + shared name-resolution helpers + try_bind, all reusing the
engine's gates — no second validator), the GraphSession/Op/replay per-op
surface, build-free unwired/vocabulary introspection, and the acceptance-1
replay-equals-GraphBuilder compile-identity test. The JSON op-list + CLI are
iteration 2. Eight RED-first tasks; struct literals + filter strings verified
against the current tree.
refs #157
The introspectable, fail-fast construction service (#157): a declarative,
replayable by-identifier op-script that builds a runnable blueprint through
validated ops and emits the #155 value, reusing the engine's existing gates
(no second validator, C24).
Surface-agnostic core = an eager/holistic gate split: the per-op-decidable
checks (name resolution, edge kind-match, the >1-cover DoubleWiredPort arm,
param bind) fire eagerly at the offending op; the only-at-end-decidable checks
(0-cover UnconnectedPort totality, DuplicateParamPath injectivity, root-role
boundness) stay holistic at finalize. Both cadences call the SAME extracted
predicates. Plus build-free introspection over the closed std_vocabulary
(enumerable companion) and a partial document's unwired slots. The JSON op-list
+ `aura graph build`/`introspect` CLI is a thin shell (iteration 2).
Spec auto-signed under /boss on a grounding-check PASS (11/11 load-bearing
current-behaviour assumptions ratified by named green tests). Derived sub-forks
logged on the issue.
refs #157
Architect drift review of cycle 0087 (#155, the C24 blueprint-serialization-and-
loader first cut). What holds (confirmed against the diff): C17 / no-RustAst (the
format carries only type identity + name + bound params + topology; the build
closure is dropped and re-derived, no node logic in the data); invariant 9 /
domain-free engine (injected resolver, the closed match in aura-std, engine lib
deps aura-core + aura-analysis only — a compiled-in closed set, not a dynamic
registry); C9 round-trip / C23 bootstrap untouched (loader's terminal step is the
unchanged Composite::new -> bootstrap_with_params; validation deferred to the
existing gate, not bypassed).
Resolution:
- fix (ledger): C24 Status refreshed from "unbuilt" to "first cut shipped"
with a cycle-0087 realization note; C9's round-trips-both-ways refinement
marked realised. The ledger no longer contradicts the code.
- backlog (debt-med): the hand-written std_vocabulary match has no guard test
that fails when a new zero-arg aura-std node lacks an arm — fails safe today
(clean UnknownNodeType, never silent-wrong). Filed as #160.
- carry-on (scaling): construction-arg builders + sinks excluded, so no real
harness round-trips yet; harness retirement blocks on the construction +
project-as-crate layers — already tracked #156/#157/#159.
No architect-sweep / regression script is declared in the project facts, so the
suite is the gate: cargo test --workspace 748 passed (737 -> 748 = the 11 new
tests); clippy --all-targets -D warnings clean.
Ephemeral cycle artifacts removed (lifecycle convention): docs/specs/0087-*,
docs/plans/0087-*.
Five tasks for #155: serde derives on the serialized plain types; the closed
aura-std std_vocabulary resolver (22 zero-arg builders); the DTO layer +
canonical serializer + format_version envelope; the resolver-injected loader +
typed LoadError; and the round-trip tests (canonical golden, named failures,
bit-identical run vs Rust twin, idempotence, recursion).
refs #155
The C24 first cut (#155): a stable, versioned, canonical serialization of a
param-generic Composite blueprint plus the data -> blueprint -> FlatGraph
loader, closing the graph-as-data loop. Format references nodes by compiled-in
type identity over a closed vocabulary (no logic, non-Turing); the loader is
generic over an injected resolver with the concrete aura-std vocabulary match
living outside the engine (engine stays domain-free). Acceptance: a serialized
blueprint runs bit-identical (C1) to its Rust-built twin.
Signed under /boss on the Step-5 grounding-check PASS (all load-bearing
assumptions ratified by currently-green tests).
refs #155
Settle the #109 fork as topology-as-data (game-engine principle, C16): node logic stays Rust (C17), topology becomes a serializable value the World owns, generates, structurally searches, and reproduces. New contract C24 + refinements to C9/C17/C18/C20/C21/C22; threads: #109 resolved -> C24. Format itself unbuilt (next brainstorm/milestone).
refs #109
Architect drift review (8d04a84..HEAD): clean, no drift/debt.
What holds (verified against code, not asserted):
- C10 cost contract preserved: the non-R-harness guard reads the *resolved*
harness, so a `run` cost flag on sma/macd (incl. the implicit default) is
exit-2 fail-loud, never a silent no-op; parse_nonneg_rate names the flag;
the eight stage1-r cost goldens are byte-unchanged.
- The three edited realization notes are grounded: C12 Arc-sharing →
aura-ingest/src/lib.rs:316; aura-std "~30 modules" → 30 node files;
C15 Session emits only bars_since_open, matching the quoted source wording.
- project-layout.md rewrite complete: no residual exposure/realistic-broker.
Non-actionable scaling note (architect): the guard hardcodes Stage1R as the
sole R-harness, coupled to the "stage1-r only" note text — three sites move
together if a second R-harness ever lands; not in plausible next scope.
Regression gate: cargo test --workspace exit 0 (48 binaries, 737 passed, 0
failed); cargo clippy --workspace --all-targets -- -D warnings exit 0. No
baseline moved → no ratify needed. cycle 0086 tidy (clean).
Ephemeral spec/plan (docs/specs/0086, docs/plans/0086) removed per the
ephemeral-artifact convention.
#153 (friction surfaced by the milestone-#148 close fieldtest). Three cost-flag
fixes on `aura run`, all on the pure parse_run_args grammar:
- Units discoverable: a COST_FLAGS_NOTE appended to the run usage + --help
(price units, charged in R as cost/|entry-stop|, per-ENGINE-cycle carry,
>= 0, stage1-r only).
- A negative rate names the cause + flag ("<flag> must be non-negative, got <v>")
via a factored parse_nonneg_rate helper, not a bare usage dump.
- A cost flag on a non-R harness (sma/macd) is refused (exit 2, "cost flags
require an R-evaluator harness (stage1-r)") instead of silently ignored — the
named spec_gap, decided reject/fail-loud (refuse-don't-guess, recorded on #153),
pinned by a new C10 ledger note.
Internal RunArgs/RunData/HarnessKind gain #[derive(Debug)] (behaviour-neutral) so
the pure parse_run_args unit tests can use expect_err.
Verified independently: cargo test -p aura-cli green, the eight stage1-r cost
goldens byte-unchanged; clippy -p aura-cli --all-targets -D warnings clean. Only
the grammar/usage strings and the previously-unguarded sma-cost path changed;
the cost-model graph and R math are untouched.
closes#153
Four tasks from spec 0086: (1) named non-negativity diagnostic via a
parse_nonneg_rate helper + perturbed-test fix; (2) non-R-harness guard
(the spec_gap, reject/exit-2); (3) COST_FLAGS_NOTE appended to the usage
+ --help surfaces; (4) the C10 ledger note + full-suite/clippy gate.
Confined to crates/aura-cli; the eight stage1-r cost goldens stay byte-green.
refs #153
Settled design from #153 (fieldtest friction of milestone #148): make the
`aura run` cost-flag surface honest — a units/constraints note in the usage,
a named non-negativity diagnostic, and a refuse-don't-guess rejection of cost
flags on a non-R harness (the spec_gap; decided reject/exit-2, recorded on
#153). CLI-ergonomics + one C10 ledger note only; no cost-model/R-math change.
Boss-signed: grounding-check PASS (independent fresh-context agent; 6/6
load-bearing assumptions ratified by currently-green tests).
refs #153
The "day in the life" worked example was stale on the #117 (exposure→bias)
and #116 (realistic broker retired → cost-model graph in R) reframes.
Rewrite steps 3/4/7 to the live model: unsized bias stream → in-R
risk-executor + R-evaluator (E[R]/SQN) → optional cost-model graph
(net R = gross R − cost-in-R); money/real-broker pushed to the live
deploy edge; the matrix axis swapped from the retired {sim-optimal,
pepperstone} broker pair to a {fixed-stop, vol-stop} risk-executor axis.
closes#151
Three drift items surfaced by the 2026-06-29 design-goal audit, all
recording shipped reality into the ledger (no contract change):
- C12: cross-sim `Arc<[T]>` sharing is no longer "still open" — the
sweep/MC/walk-forward families build members over one shared
`Arc<DataServer>`/`FileCache`, so a window is parsed once and shared
zero-copy across the disjoint sims (aura-ingest/src/lib.rs:316).
- open-threads: `aura-std` is no longer "doc-only" — ~30 node modules.
- C15: record that `Session` deliberately ships only `bars_since_open`
(the node's own contract), the other two streams deferred (refs #154).
Reconcile 68 terms against algotrading frameworks (LEAN, NautilusTrader,
backtrader, VectorBT, Zipline), quant-validation literature (Van Tharp,
Bailey & Lopez de Prado, White), and stream/dataflow vocabulary. Conservative
pass: extend Avoid lists with industry synonyms/homonyms and fold short
disambiguation notes into definitions; no canonical headings renamed.
Touched 18 entries. Avoid additions: overfit probability (PBO/CSCV), signal
(entry/exit signal, trade signal), position table (transactions, trade
blotter), conviction-based risk allocation (risk budgeting/parity/
contribution), run registry (model registry), session node (session window),
sign-agreement (market breadth). Disambiguation notes: broker vs fill/account
simulator, edge (wiring/trading/deploy senses), cycle (clock step vs DAG
loop), manifest vs Cargo.toml config, experiment vs MLflow container,
run-count (version counter), bias vs statistical bias, bootstrap vs
statistical bootstrap, deflated score anchored on the Deflated Sharpe Ratio,
Monte-Carlo (input not trade sequence), World vs Unreal UWorld.
Deferred (out of scope this pass): heading renames, a new `record` entry +
composite sense-split, the playground egui->web wording, the SoA Avoid line.
Cycle 5 of milestone #148 (per-cycle-held accrual + CarryCost). Architect drift
review over 39f9387..f5e00a9: status clean, no code drift.
What holds (architect, evidence-of-review):
- C11 byte-identity: the AtClose eval arm is the pre-cycle-5 tokens verbatim
(if closed { per } / if open { per }, the per + cum lines unchanged), so the
cycle-0083/0084 net_expectancy_r goldens (-614.3134020253314, -615.0304388396047)
stay byte-identical and the AtClose unit regressions are untouched.
- Accrual sound, no double-count: aura-analysis (summarize_r) is genuinely unchanged.
During a hold the carry lives only in open_cost_in_r (cum=0); at close it moves to
cum while open_cost->0 (open/closed mutually exclusive per cycle), so net_r_equity
subtracts cum+open singly. summarize_r reads the close-row dumped total + the
window-end accrued-so-far — one charge per trade, acc reset proven. The 3-field
cost record's semantics generalize cleanly to accrual.
- C9/C16/C23: the diff touches only aura-std + aura-cli; aura-engine stays
domain-free. CarryCost is a CostNode factor, CostRunner a plain Node, composed via
cost_graph/CostSum. label() carries its param. All three run_stage1_r call sites
threaded; the negative-rate guard is symmetric with the sibling cost flags.
Resolved this commit (the cycle-close ledger sync):
- C10 realization note (cycle 0085) added under docs/design/INDEX.md — C10's
"per-cycle-held factors accrue over the hold" clause is now first realized (the
prior 0084 note covered the cost-graph composite-builder).
Regression gate: cargo test --workspace (0 failures), cargo build --workspace, cargo
clippy --workspace --all-targets -- -D warnings clean — the architect is the primary
gate (no dedicated regression script). Ephemera (spec 0085 + plan 0085) git rm'd.
Milestone "Cost-model graph (in R)" remains OPEN.
refs #148
Parent spec docs/specs/0085 (19ba1ec). 4 tasks: (1) ChargeMode + the PerHeldCycle
accrual branch on the shared CostRunner (AtClose arm kept verbatim for byte-identity)
+ 2 RED B-proof unit tests; (2) the CarryCost node (ConstantCost twin, PerHeldCycle)
+ lib.rs registration; (3) the --carry-per-cycle CLI flag threaded through every site
(incl. the easy-to-miss main.rs:4446 bin unit-test call site); (4) two capture-then-pin
net_expectancy_r goldens + the net_r_equity-bleeds-over-the-hold integration B-proof.
summarize_r and the CLI net_eq wiring are UNCHANGED; the cycle-0083/0084 goldens are
the byte-identity regression net.
refs #148
Cycle 5 of milestone #148 (cost-model graph in R): the per-cycle-held accrual
mechanism — C10's "per-cycle-held factors accrue over the hold" — exercised by a
constant per-held-cycle CarryCost node and a --carry-per-cycle CLI flag.
Approach B (user-decided, "ja, mach B"): the headline net_r_equity curve bleeds
continuously over each hold, not stepping at close. Mechanism (derived, logged on
#148): the bleed lives in open_cost_in_r (grown each held cycle = accrued-so-far
mark-to-market); cost_in_r dumps the accrued total into cum at close. The
net_r_equity tap already subtracts both cum_cost_in_r and open_cost_in_r, so the
curve bleeds with NO change to summarize_r or the CLI net_eq wiring — and the
AtClose runner branch is kept verbatim, so every cycle-0083/0084 net_expectancy_r
golden stays byte-identical. ChargeMode is a property of the factor read by the one
shared CostRunner (no second runner type); CarryCost is a ConstantCost twin
differing only in charge_mode() -> PerHeldCycle.
Auto-signed under /boss on a grounding-check PASS (all 6 current-behaviour
assumptions ratified against named green tests). Reference issue #148 carries the
A/B fork (user decision), the minimal-scope decision (derived), and the mechanism
refinement (derived).
refs #148
Record-reality add: 'tap' is in consistent use across the code
(ColumnarTrace.tap, the --tap flag, the equity/exposure/net_r_equity/
r_equity taps) and the design ledger, but had no glossary entry. The
entry also draws the line the 'tap/sink' shorthand blurs: a sink is
the recording-role node, a tap is the named stream it records.
Cycle 4 of milestone #148 (cost-graph composite-builder). Architect drift review
over fc52b4f..d5c44dd: drift_found, every item dispositioned.
What holds (architect, evidence-of-review): C16/C9 — cost_graph lives in
aura-composites, the factors in aura-std, aura-engine stays domain-free; the
composite is ordinary downstream nodes. C11 behaviour-preservation is structural,
not just asserted — the composite inlines to the same flat fan-in as the deleted
CLI block (identical node set/order, cost[k].field slot map, geometry fan, net_eq
terms/signs, cost_rec cols), so the cycle-0083 net_expectancy_r goldens stay
byte-identical. C23 — role/port names non-load-bearing; 4 fixtures pin the role-set
+ output triple.
Resolved this commit:
- [high] ledger C10: the cycle-0083 note's "Still deferred (decision E)" is removed
(E shipped this cycle) and a cycle-0084 realization note added.
- [medium] cost_graph .leak() provenance (aura-composites/src/lib.rs): the
"risk_executor precedent" was imprecise — that .leak() lives in risk_executor's
TEST (one-shot, leak-safe), not its production builder. Doc corrected: the leak is
fine for one-shot run-path construction but must be interned (the COL_PORTS
production pattern) before cost reaches the sweep path.
- [low] cli_run.rs golden docstring cited the deleted `slot * COST_WIDTH + f` CLI
wiring; updated to the cost_graph composite's per-node cost[k].<port> wiring.
Deferred (tracked, not pending) -> #152:
- [medium] the cost[k].<port> index-namespacing restated across CostSum / cost_graph
/ the CLI (a build-validated lockstep — a divergent name fails loudly at g.build(),
NOT the silent positional kind 0083 collapsed), AND the per-build .leak() that
becomes an allocation regression on the per-member sweep path. Both are harmless on
the current run-only cost path; the proper fix is one sweep-safe single-source
cost-port-name contract (interned), best built with the deferred sweep-cost cycle.
Regression gate: cargo test --workspace (0 failures), clippy --workspace --all-targets
-- -D warnings clean, cargo doc clean — the architect is the primary gate (no dedicated
regression script). Ephemera (spec 0084 + plan 0084) git rm'd.
Milestone "Cost-model graph (in R)" remains OPEN.
refs #148
Two tasks: (1) the cost_graph(Vec<PrimitiveBuilder>) -> Composite builder in
aura-composites + the GEOMETRY_WIDTH re-export from aura-std + two unit tests
pinning the exposed input-role set and the 3-field output; (2) the aura-cli
stage1_r_graph rewire onto cost_graph, deleting MAX_RUN_COST_NODES and
COST_SUM_PORTS. Runtime port names .leak()'d (the risk_executor precedent).
Behaviour-preserving: the cycle-3 net_expectancy_r goldens are the regression net.
refs #148
Cycle 4 of milestone #148 (Cost-model graph in R), decision E — the cost-graph
composite-builder, recorded across cycles 2-3 as the natural next.
Adds a `cost_graph(Vec<PrimitiveBuilder>) -> Composite` builder in aura-composites
that fans the 4 PM-geometry inputs to N cost nodes, surfaces each node's extra
inputs as `cost[k].<port>` roles (schema introspection past GEOMETRY_WIDTH),
sums them via CostSum, and exposes the 3-field aggregate. Replaces the CLI's
manual slot-indexed cost-wiring + the hardcoded MAX_RUN_COST_NODES=2 cap with
one principled composite of arbitrary arity. Re-exports GEOMETRY_WIDTH from
aura-std (first cross-crate consumer). Behaviour-preserving at the value level:
the composite inlines at bootstrap (C11) to the same flat computation, so the
two cycle-3 net_expectancy_r goldens, the C18 no-cost golden, and the full suite
stay green verbatim.
Routed spec-driven (specify direct-entry): settled in #148's scope + the mature
aura-composites idiom + the CostNode contract. All four forks (composite API
shape, extra-input namespacing, GEOMETRY_WIDTH visibility, CLI consumption) are
derived decisions logged on #148; none hangs on pure user preference.
Auto-signed under /boss on the Step-5 grounding-check PASS (every load-bearing
current-behaviour assumption tied to a named green test or current source; no
with-cost graph label/shape pin contradicts the C23-permitted label drift).
refs #148
Architect drift review over 6c7f5dd..HEAD. No regression scripts in this project
→ the architect is the sole gate; verdict: shipped code byte-clean.
What holds (verified against the diff):
- C9 — a CostRunner<F> is a plain downstream Node; the two cost nodes are thin
factors wired through the same cost_node_builder. No runtime sub-object.
- C18 / byte-identity — the numerator/latched token form is preserved verbatim;
the two new CLI goldens pin exact net_expectancy_r and pass; the no-cost golden
is untouched.
- C10 co-temporality + C23 + invariant-4 — the gate-on-PM-geometry-only rule
(cold factor input → 0 cost, row still emits) is now centralized once in the
runner; name() dropped cleanly (label() remains the non-load-bearing symbol);
the 3-field triple is a single source (COST_FIELD_NAMES) read by both producers
+ CostSum + the CLI — the cycle-0082 by-convention lockstep is structurally gone.
Resolution:
- [fix] docs/design/INDEX.md C10 — added the cycle-0083 realization note (each
prior cost cycle got one; the deferred general CostNode trait is now shipped and
the lockstep eliminated). Ledger deferred-work tracking re-synced.
- [fix] crates/aura-std/src/lib.rs:5 — module doc now names the CostNode/CostRunner
authoring surface.
- [carry-on, low debt] vol_slippage_cost.rs still declares its extra-input port
twice (the cost_node_builder arg and the trait extra_inputs()); a clean unifier
needs design thought (builder() is static, no factor instance) and is not worth
forcing at cycle close. Documented here; agreement rests on the helper + a test +
the harness debug-assert.
Cycle 0083 ephemera removed (docs/specs/0083 + docs/plans/0083).
Verified: cargo build/test --workspace clean (0 failures), clippy -D warnings
clean, cargo doc -p aura-std clean (CostNode/CostRunner intra-doc links resolve).
refs #148
Cycle 3 of the cost-model-graph milestone: lift the duplicated cost-node
skeleton (shared verbatim by ConstantCost/VolSlippageCost, locked by convention
against CostSum) into a CostNode factor trait + a generic CostRunner<F> adapter,
with one source of truth for the 3-field cost record. Behaviour-preserving: same
schemas, same wiring, byte-identical output; the existing suite is the regression
net. Cost-graph composite-builder deferred (decision E).
Grounding-check PASS (7/7 assumption groups against named green tests).
Fork decisions logged on the reference issue.
refs #148
Cycle 0082 (cost-graph composition, cycle 2) closes drift-clean against C10. The
architect review confirms the cycle is fundamentally sound:
What holds: VolSlippageCost + CostSum are ordinary aura-std downstream nodes
(C9/C8), cost stays out of aura-engine; the vol proxy is a trailing realized range
(C2, no look-ahead); feed-forward, no equity feedback (C1); the co-temporality
contract (gate cost nodes on PM geometry only, 0-cost on a cold state input) is
consistent with C1/C2/C9 and the cycle-1 positional join, and charging 0 during
warm-up is honest (no estimate yet, not a fabricated guess). The no-cost C18 golden
is byte-identical.
Drift resolved at close:
- [medium -> fixed] docs/design/INDEX.md (C10): added the cycle-0082 realization
note. The load-bearing co-temporality contract (generalizes to all future cost
factors) lived only in code comments + spec 0082; the ledger is now its durable
home once the spec is removed.
- [low -> fixed] crates/aura-cli/src/main.rs: --cost-per-trade now rejects negatives
at parse, symmetric with --slip-vol-mult (it previously deferred to the node
assert's panic). Run-path tests + the no-cost golden stay green.
Carry-on (documented deferrals, not this cycle's debt to fix):
- The 3-field cost triple {cost_in_r, cum_cost_in_r, open_cost_in_r} is restated
by-convention across ConstantCost, VolSlippageCost, and CostSum (compiler-unlinked
lockstep) — to be unified by the general CostNode trait, now justified by two
concrete nodes (#148).
- SLIP_VOL_LENGTH=5 is sized to warm within the synthetic smoke fixture; documented
in the const doc (on real M1 data any reasonable window warms).
- A future per-cycle-held accrual (carry/funding) accrues over holding duration,
which will force a summarize_r fold change and stress the positional seam — flagged
for that deferred cycle.
Regression gate green and unchanged: full workspace suite 0-fail, clippy --workspace
--all-targets -D warnings clean. No baseline moved -> no ratify.
Spec + plan ephemera removed (git rm docs/specs/0082, docs/plans/0082).
refs #148
Cycle 2 of the "Cost-model graph (in R)" milestone (#148): the milestone's real
architectural claim — the cost graph composes. Two cost nodes now sum into one
net-R curve while summarize_r and the net_r_equity tap stay structurally
unchanged.
- VolSlippageCost (aura-std): a second, STATE-DEPENDENT cost node; per-trade
charge = slip_vol_mult * volatility / |entry-stop|, in R. The vol is an
independent short-horizon realized range (SLIP_VOL_LENGTH=5, distinct from the
stop's EWMA-3) — scaling slippage by the stop's own vol would collapse cost-in-R
to a constant (indistinguishable from ConstantCost).
- CostSum (aura-std): the cost-graph OUTPUT node — sums N cost nodes' 3-field
cost-in-R records per-field into one aggregate. summarize_r and net_r_equity
read the aggregate (one home for cost; n=1 is the identity, so the cost path is
uniform). A future CostNode trait (deferred) will unify the cost-triple the two
producer nodes currently restate by-convention.
- Run path: --cost-per-trade and --slip-vol-mult combine, their costs summing into
the net-R curve; a hoisted vol proxy (RollingMax-RollingMin) keeps the single
feed. Run-path-scoped; sweep/walkforward/mc pass None.
Co-temporality contract (the load-bearing design decision; corrected from the
signed spec after the implement-loop correctly BLOCKED Task 3 on it). summarize_r
positional-joins cost[i] <-> record[i], so the cost stream must be co-temporal
1:1 with the PM record. A cost node is therefore gated ONLY by the PM geometry
(closed/open/entry/stop); a not-yet-warm state input (the vol proxy warms later
than PM) contributes 0 cost that cycle rather than withholding and desyncing the
stream. This makes co-temporality structural + warmup-independent, preserves the
C18 golden, and generalizes to any future cost factor. The rejected alternative
(a key-join in summarize_r) would have moved the golden and pushed cost-graph
logic into the post-run fold. Recorded on #148.
Tests: VolSlippageCost + CostSum unit sets (incl. the co-temporal-zero-during-
warmup case); the CLI composition run (both flags -> net_both < net_flat,
net_r_equity persisted); node-level EXACT additive composition (net_both ==
net_flat + net_vol - gross over the real nodes), the aggregate net_r_equity ==
post-run net total, and CostSum(1) identity. Full workspace suite green; clippy
-D warnings clean; the no-cost C18 golden byte-identical (the regression floor).
Deferred (later cycles of this milestone): the general CostNode trait + cost-graph
composite-builder (now justified by two concrete nodes), the conviction-weighting
R-aggregation axis, and cost on the reduce-mode sweep path. Spec + plan amended to
the corrected contract; both are cycle ephemera (git rm at cycle close).
refs #148
Task-by-task plan for spec 0082: the VolSlippageCost node (Task 1) and the
CostSum aggregator (Task 2) in aura-std; the run-path wiring as one compile-gate
task (Task 3) — stage1_r_graph cost-param widening + vol-proxy hoist + the
CostSum-aggregated cost block + the --slip-vol-mult thread-through across all
call sites, with a CLI composition test and the no-cost golden floor; and the
node-level exact-sum composition tests (Task 4). RED-first per task. The
&'static str CostSum port names are interned (COST_SUM_PORTS, mirroring
COL_PORTS); summarize_r is unchanged (it folds the CostSum aggregate).
refs #148
Cycle 2 of the "Cost-model graph (in R)" milestone (#148): the milestone's
real architectural claim — the cost graph composes. Two pieces:
- VolSlippageCost (aura-std): a second, state-dependent cost node whose
per-trade charge scales with a measured volatility input (k * vol /
|entry - stop|), distinct from ConstantCost's flat charge. The vol is an
independent short-horizon realized range, deliberately NOT the stop's own
vol — scaling slippage by the vol the stop already normalizes would collapse
cost-in-R to a constant (indistinguishable from ConstantCost).
- CostSum (aura-std): the cost-graph output node — sums N cost nodes' 3-field
cost-in-R records per-field into one aggregate record. So summarize_r and the
net_r_equity LinComb(4) read the AGGREGATE and stay structurally unchanged
(one home for cost; n=1 is the identity, keeping the cost path uniform).
Run-path wiring: --cost-per-trade and --slip-vol-mult combine, their costs
summing into the net-R curve; a hoisted vol proxy keeps the single-feed pattern.
A no-cost run stays byte-identical (C18 golden floor). Scope is the run path;
sweep/walkforward/mc still pass None (sweep-path cost remains deferred), and the
general CostNode trait stays deferred (designed against two shipped nodes later).
Auto-signed under /boss on the grounding-check PASS: all 8 load-bearing
existing-behaviour assumptions tie to named green tests. Fork decisions recorded
on #148.
refs #148
Cycle 0081 (cost-model graph, cycle 1) closes drift-clean against C10: the
architect review confirmed ConstantCost + the net-R seam realize the contract
at its floor (C9 cost node; net_r_equity a C8/C18 sink; R-pure; one home for
cost, subsuming the retired scalar round_trip_cost; pure feed-forward C1), and
the no-cost baseline is byte-identical (C18 golden held). Regression gate green
and unchanged: full workspace suite 0-fail, clippy --workspace --all-targets
-D warnings clean. No baseline moved -> no ratify.
Ledger sync (docs/design/INDEX.md): added the C10 cycle-0081 realization note
(ConstantCost node + net_r_equity seam shipped; scalar round_trip_cost retired;
run-path-scoped; later-cycle deferrals named).
Drift swept (cycle-adjacent + cheap one-liners):
- crates/aura-analysis/src/lib.rs net_expectancy_r doc: the removed in-fold
mechanic (mean(R - round_trip_cost/latched_dist)) -> the cost-stream fold.
- crates/aura-std/src/lib.rs module doc: "realistic broker profiles" (retired
by #116) -> the legacy SimBroker pip yardstick + cost-model nodes.
Backlogged (pre-existing, broader, predates this cycle): the
docs/project-layout.md backtest worked example is stale on #117 (exposure->bias)
and #116 (realistic broker); a proper rewrite is filed as #151, not folded here.
Architect-accepted: the #[allow(dead_code)] on r_col::ENTRY_PRICE/STOP_PRICE
(their production reader legitimately removed when summarize_r stopped recovering
latched_dist; still the layout contract + exercised by test fixtures).
Spec + plan ephemera removed (git rm docs/specs/0081, docs/plans/0081).
refs #148
Task-by-task plan for spec 0081: the ConstantCost node (aura-std), the
summarize_r scalar->cost-stream change (the workspace-wide call-site
migration as one compile-gate task), and the run-path wiring (net_r_equity
tap + --cost-per-trade) — RED-first per task. Scoped to the run path
(non-reduce); sweep/walkforward/mc pass None this cycle.
refs #148
Cycle 1 of the "Cost-model graph (in R)" milestone (#148): a single
ConstantCost node (flat round-trip cost per trade) plus the net-R seam —
an in-graph cost_in_r stream, a net_r_equity sink (sibling of r_equity),
and summarize_r folding that one stream into net_expectancy_r (replacing
the scalar round_trip_cost). A run with no cost stays a byte-identical
gross-R baseline. Defers the general CostNode trait, the cost-graph
composite-builder, data-grounded factors, and the conviction axis.
Auto-signed under /boss on the grounding-check PASS: all load-bearing
existing-behaviour assumptions are ratified by green tests. Fork
decisions recorded on #148.
refs #148
Follows the C10 rework (29cdc8c). C15: the deploy-grade geometry fields now defer
to the cost model's currency→R normalization / the live deploy edge, not the
retired Stage-2 realistic broker. Glossary: "realistic broker" and "Stage 1 /
Stage 2" marked superseded (kept as history); bias/broker/equity-stream/position-
table/R/R-metrics/R-evaluator/RiskExecutor/Sizer/sim-optimal-broker/veto reworked
to the cost-model-in-R model; added cost model, gross R, net R, net-R equity
curve, conviction-based risk allocation.
Reworks the C10 contract per a ratified in-context design discussion (#116). The
historical "realistic broker" with authored frictions is retired as
horseshoe-throwing: real friction (slippage, swaps, spread) is not historically
knowable; the only reliable friction is measured forward against a real broker.
In its place:
- Cost is a composable C9 graph of cost nodes, in R, that approximates (never
claims) realism — generalizing/subsuming the scalar round_trip_cost. Output is
a net-R equity curve (gross R = signal, net R = after cost).
- Research is pure feed-forward: the Sizer and compounding leave the loop; the
z^-1 fill-edge register and the flat-1R-vs-compounding structural axis are gone.
Compounding/Kelly/drawdown are post-hoc transforms at the deploy/account layer.
- Executor = stop-rule + position-management in R. size/volume is a deploy
concept; the position-event table is demoted to a deploy/reconciliation artifact.
- Conviction-based risk allocation survives as an R-aggregation axis (flat vs
|bias|-weighted), distinct from the removed money-Sizer.
- Money, a real broker, and cTrader Open API are a separate, later live/deploy
edge (C11 record-then-replay; C13 frozen deploy) — the only ground truth.
Stage-1 R realizations preserved as history; the #117 reframe's R spine survives,
its Stage-2/currency/register/compounding portions superseded.
Cycle 0080 was a 44-line behaviour-preserving re-export relocation along the
exact engine↔domain seam the 0079 architect review just blessed, so the drift
review is carried from 0079 rather than re-dispatched; the only drift introduced
is the ledger note, synced here. Regression gate green and unchanged: full suite
665/0 (incl. the C18 goldens), clippy --workspace --all-targets -D warnings
clean.
Ledger sync (docs/design/INDEX.md): added a cycle-0080 realization note —
FamilySelection/SelectionMode are now in aura-analysis (engine-side extraction
complete); recorded the user-ratified decision that the aura-registry
Metric/metric_cmp/deflation vocabulary stays in the registry by design (it is
the trading selection layer, its deflation statistics branch irreducibly on
metric identity, and genericity would be a one-implementor abstraction); and
that making RunReport generic / a domain-agnostic orchestration substrate is
deferred to the World/C21 layer, explicitly out of #136.
No baseline moved (the suite is the regression gate; green-unchanged), so no
ratify statement. No spec/plan ephemera to drop.
Architect drift review (3e8fd73..HEAD): the extraction is faithful and
behaviour-preserving — every moved symbol's definition left report.rs, 35 unit
tests relocated with their subject, aura-engine/src/lib.rs byte-unchanged, the
four downstream crates byte-unchanged. No code defect. Regression gate green:
cargo build/test --workspace = 665 passed / 0 failed (incl. the C18 cli_run
goldens and the registry legacy-line loads); clippy --workspace --all-targets
-D warnings clean.
Ledger sync (docs/design/INDEX.md) — pre-existing crate-topology drift the
extraction made visible:
- C10 notes (cycles 0063/0068) placed PositionAction / PositionEvent /
derive_position_events "in aura-engine beside RunMetrics / summarize_r";
cycle 0079 moved all of them to aura-analysis — corrected, with a forward
pointer to the new C16 note.
- C10 cycle-0065 note claimed "RiskExecutor ships as a public aura-engine
composite-builder" and "Composites live in aura-engine ... aura-engine ->
aura-std a normal dependency"; both stale — composites live in
aura-composites and aura-std is now an aura-engine [dev-dependencies] only.
Corrected.
- Added a C16 realization note recording the aura-analysis extraction as an
architecture fact: the engine<->domain seam, the current five non-node
workspace crates, and the deferred #136 generic-over-M tail.
serde_json moved from [dependencies] to [dev-dependencies] in aura-analysis
(architect debt item): the production types derive serde, but JSON rendering
lives in aura-engine's RunReport::to_json, so serde_json is test-only here and
must not enter the frozen artifact's library link (C16 frozen-artifact
scrutiny). Build + the 35 crate tests stay green after the move.
No baseline moved (the suite is the regression gate; it stayed green), so no
ratify statement. No spec/plan ephemera to drop (compiler-driven / agent-driven
cycle — no docs/specs or docs/plans files were created).
Resolves the four non-bug findings from the inferential-validation milestone
fieldtest (the bug — the broken-pipe panic — is fixed in 1088320):
- [friction] generalize refusals now carry a reason instead of a bare usage dump:
<2 instruments, a multi-value candidate flag, a missing knob, a duplicate
symbol, and a non-stage1-r strategy each get a one-line why (mirroring the
R-only metric refusal). Two parser tests strengthened to pin the message.
- [friction] --select <argmax|plateau:mean|plateau:worst> added to the
walkforward branch of the global USAGE (aura --help) — the plateau objective
was undiscoverable from the only help the CLI prints (the per-subcommand usage
already carried it).
- [spec_gap] C1 gains a scope note: bit-identity is per-run; a derived metric
recomputed by two different command paths may differ by floating-point
reassociation (<=1 ULP), which is not a C1 violation — ratifies the
fieldtester's reading of the sweep-vs-generalize sqn difference.
- [spec_gap] the milestone vocabulary is added to docs/glossary.md:
cross-instrument generalization, deflated score, overfit probability,
neighbourhood score, plateau selection, sign-agreement, worst-case floor.
The fieldtest triage spec (docs/specs/fieldtest-*) is removed now its findings
are resolved; the tracked fieldtests/ scenario corpus stays.
Verified: cargo test --workspace green (0 failed), clippy --workspace
--all-targets -D warnings clean; `aura --help` shows --select; the refusals
print their reason.
refs #146
Milestone-close gate for "Inferential validation (defend against false
discovery at sweep scale)". Downstream-consumer exercise from the public
interface only (CLI --help, ledger, glossary, examples — never the source).
All four end-to-end scenarios PASS — the milestone delivers its promise:
trials-deflation (#144), plateau-over-peak (#145), and cross-instrument
generalization (#146) all reach the output and read sensibly, and the
composed "is this edge real or overfit?" verdict is honest (scenario 4: a
GER40 in-sample winner sqn 0.994 does NOT generalize — worst_case -0.149 on
EURUSD, sign_agreement 2/3 → instrument-specific / likely overfit).
Milestone status roll-up: bugs_found — NOT green, so the milestone is NOT yet
closeable (the green roll-up is the functional leg of the close gate). Findings:
- [bug] a family-emitting command piped into an early-closing reader
(`aura sweep|walkforward|mc|runs family … | head`) panics "Broken pipe
(os error 32)" on stdout (exit 101 under pipefail) instead of exiting
cleanly on EPIPE. Pre-existing (not a #146 regression — reproduced on
`aura sweep | head -1`); surfaced by the milestone commands. -> debug (RED-first).
- [friction] two generalize refusals (single --real symbol; multi-value
candidate flag) dump bare usage with no reason. -> tidy iteration.
- [friction] the --select plateau objective is absent from `aura --help`. -> tidy.
- [spec_gap] sweep vs generalize recompute the same member's sqn with a stable
1-ULP difference; the ledger's C1 / "recomputable" is silent on cross-command
metric agreement. -> ratify (ledger note).
- [spec_gap] the milestone vocabulary (deflated_score / overfit_probability /
worst_case / sign_agreement) is absent from docs/glossary.md. -> docwriter.
Artefacts: docs/specs/fieldtest-milestone-inferential-validation.md +
fieldtests/milestone-inferential-validation/ (README + 4 scenario scripts).
The existing fieldtests/ tree was untouched.
refs #146
Architect drift review (d50a482..feat): drift_found — documentation only,
NO contract violation. C1/C9/C10/C23 verified preserved against the diff:
`generalization` is a pure deterministic fold (registry-side, C9; R-only,
C10); the sole `instrument = Some` production site is `run_generalize`, every
other manifest constructor threads `None` + `skip_serializing_if`, so
sweep/mc/walkforward/standalone manifest bytes are byte-identical (C23); the
compat read-mirror was threaded in lockstep.
Drift fixed in this commit (ledger sync, docs/design/INDEX.md):
- The inferential-honesty thread read "now two of three pieces built" / "#146
remains the last open piece" — false now that #146 landed. Updated to record
the inferential half as structurally complete (all three built, cycle 0078)
+ the #146 realization, including the aggregator/validator-vs-selector
distinction (#146 is a recomputable aggregate, not a FamilySelection
annotation).
- C18 realization block: added the cycle-0078 note — the comparison axis (C12)
realized as FamilyKind::CrossInstrument + the first-class RunManifest.instrument
lineage field.
Regression: no regression scripts configured (project facts name only
Build/Test/Lint/Doc); architect is the gate. cargo test --workspace green
(0 failed) and cargo clippy --workspace --all-targets -D warnings clean,
orchestrator-verified.
Ephemera removed per the project lifecycle convention: docs/specs/0078-* and
docs/plans/0078-* (git rm). Durable rationale lifted to the ledger above.
refs #146
Three tasks for #146: (1) RunManifest.instrument serde-widened field +
compat-mirror lockstep + thread all in-workspace literals + back-compat tests;
(2) aura-registry FamilyKind::CrossInstrument + the R-only generalization
reduction (worst-case floor + sign-agreement + breakdown) + check_r_metric +
two RegistryError variants; (3) aura-cli generalize subcommand (single-cell
stage1-r candidate x instrument list), data-free arity/metric refusals, a
gated-skip cross-instrument E2E.
fieldtests/ confirmed outside the workspace compile set; FamilyKind has no
exhaustive match (serde-derive); only RegistryError's Display match is a gate.
refs #146