Commit Graph

529 Commits

Author SHA1 Message Date
Brummel e25ca15be4 plan: 0092 run-from-blueprint
Five-task decomposition of #165 (run a serialized signal blueprint):
1. behaviour-preserving restructure of stage1_r_graph into stage1_signal() +
   wrap_stage1r() (the serializable signal boundary + the shared wrapping),
   gated by the stage1-r + bit-identical-e2e tests staying green;
2. RunManifest.topology_hash (Tier-1 optional) + thread None into the ~24
   workspace literal sites (one compile-gate);
3. the shared run_signal_stage1r seam + the sha256_hex helper (sha2 in aura-cli,
   off the frozen engine, invariant 8) + run_stage1_r gains its topology_hash;
4. the `aura run <blueprint.json>` CLI arm (.json discriminator, --params cells),
   leaving the existing harness-kind dispatch intact (#159 stays);
5. demo signal blueprint + the bit-identical / determinism / E2E tests.

Tasks 1-2 (restructure + field-add) run and verify first. Derived grounding
decisions (the restructure target, param_space-derived manifest.params, the Q4
behaviour-preserving semantics) recorded on #165.

refs #165
2026-06-30 18:27:43 +02:00
Brummel 29a519dbf6 spec: 0092 run-from-blueprint (boss-signed)
Cycle 1 of the World/C21 milestone (#165): `aura run <blueprint.json>` loads a
serialized C24 signal blueprint, wraps it in the existing Stage-1-R run
scaffolding via a shared seam extracted from run_stage1_r, compiles + bootstraps
+ runs, and emits a RunReport whose RunManifest carries a new Tier-1-optional
topology_hash (SHA256 of the canonical blueprint form, #158 anchor).

A serialized blueprint is a SIGNAL (sinks/brokers/data/clock out of the
round-trippable set, C24), so aura run WRAPS the loaded signal in a runnable
harness — the same wrap the Rust path does, shared as one function so
"bit-identical to the Rust path" is the same seam over the same signal. The
round-trip bit-identity is already proven (blueprint_serde_e2e.rs); cycle 1
lifts it to the CLI + adds topology_hash.

Boss-signed on a grounding-check PASS: all six load-bearing current-behaviour
assumptions ratified by named green tests (blueprint_from_json, the bit-identical
round-trip, the run_stage1_r compile/bootstrap/run path, RunManifest's Tier-1
optional-field pattern, canonical blueprint_to_json, window_of). Derived
wrapping decision recorded on #165. sha2 enters under the C16 per-case
dependency review (SHA256 user-settled).

refs #165
2026-06-30 18:13:38 +02:00
Brummel df228986c4 docs(ledger): C24 enforcement shift — invariant 9 moves from structural to resolver-seam
A cycle-0091 design analysis (adversarially verified) of "does invariant 9
survive lifting blueprints onto the data plane?" records a load-bearing finding
in the C24 entry.

Pre-C24 a blueprint referencing another project's node was a compile error
(topology was in-process Rust — structurally impossible). Post-C24 it is a
type-id string refused only by the injected resolver's closed set
(UnknownNodeType). The anti-NIH rationale survives (logic stays cargo+Gitea-only;
the format distributes topology-as-content, never logic, C17) and std_vocabulary
stays a compiled-in dispatch table, not a registry — but enforcement of the
engine/project boundary degrades from compiler-guaranteed to resolver-seam
discipline. Two consequences recorded: #160 becomes the data-plane face of that
boundary (no longer droppable; its own drift still fails safe), and the injected
per-project resolver is the genuinely new, deferred invariant-9 surface (a
project can express a marketplace one layer above the engine contract) — a
World/C21-layer charter point. Invariant 8 (frozen) is reframed alongside:
runtime topology generation (research plane) + frozen deploy artifact +
deterministic-once-instantiated (C1).

refs #160
2026-06-30 17:27:11 +02:00
Brummel 4a3ae05db2 test(graph_model): cover a producer backing >1 re-exported output field
#47 — close a coverage gap in the graph model serializer: a single interior
producer node backing more than one re-exported output field was never
exercised by a fixture (every existing one maps each output to a distinct
producer or a single field).

The issue was filed at cycle 0022 against `aura-cli/src/graph.rs`'s
`render_definition` / `output_binding` helper, which rendered a textual
`(n1, n2) := <producer>` tuple binding. That file and helper no longer exist —
the textual definition renderer was replaced by the JSON model emission now in
`aura-engine/src/graph_model.rs`, where the multi-OutField case is plain flat
iteration over `c.output()` (one `[name,kind]` entry + one `#N` boundary
binding per `OutField`, by `(node, field)`), not a special tuple-binding arm. So
the original branch is gone; this guards the flat emission that replaced it.

New test `multi_outfield_producer_emits_one_binding_per_field` (+ two fixtures):
a two-field producer (`hi`:f64, `lo`:i64 — distinct kinds, so the per-field
`field_kind` lookup is exercised) re-exported as both composite outputs, nested
in a root so it renders through `composite_def_json`. Asserts both fields
surface with their own kind (`"outputs":[["hi","f64"],["lo","i64"]]`) and both
bindings reference the same producer at distinct ordinals (`0.o0`/`#0`,
`0.o1`/`#1`).

Test-only; no production code touched (the behaviour was already correct).
Verified: cargo test -p aura-engine green (237 in the lib suite); clippy clean.

closes #47
2026-06-30 16:03:29 +02:00
Brummel b8b83cf1c9 feat(0091): byte-canonical blueprint emit; record the equality-surface decision
#164 — tighten the C24 "canonical, versioned" blueprint contract on two corners
the milestone fieldtest surfaced. Both forks were derivable; resolved with
rationale (recorded on #164).

Fork 1 — canonical artifact carries no trailing newline. `blueprint_to_json`
returns the JSON value with no trailing newline (647 bytes for the SMA-cross
fixture); `aura graph build` had framed it with a `println!` (648). The library
serializer is the single canonical source — the form content-addressed topology
identity (#158) hashes — so the CLI is a transport that must not mutate the
bytes: `build_cmd` now `print!`s the canonical bytes verbatim, making the CLI and
library emit paths byte-identical. RED-first: the new E2E
`graph_build_emit_is_byte_canonical_no_trailing_newline` pinned the trailing
newline (FAIL), green after the one-line change. The two cycle-0088 `.out.json`
goldens are re-recorded at 647 bytes. The loader stays lenient (a trailing
newline on input still parses, Tier-1 robustness; the milestone fieldtest's
`mt_4` tolerance check is unaffected).

Fork 2 — the canonical JSON is the blueprint equality/identity surface; no
second in-memory PartialEq/Debug is added. `Composite`/`BlueprintNode` cannot
derive them: `PrimitiveBuilder` (aura-core node.rs) holds a
`build: Box<dyn Fn(&[Cell]) -> Box<dyn Node>>` closure, neither comparable nor
printable. Equality could only be defined over the serialized data, of which
`blueprint_to_json` is already the single source; a separate in-memory equality
notion would be a redundant second source of truth and a drift hazard against
the form #158 hashes. Recorded the decision (discharges acceptance box 2's "or
JSON-string is the intended equality surface" arm) in the C24 ledger; no code
added for it.

Verified: cargo test --workspace green (51 suites, incl. the new RED->green E2E);
cargo clippy --workspace --all-targets -D warnings clean.

closes #164
2026-06-30 15:57:15 +02:00
Brummel 8bb21b1c16 docs(0088): document op-script grammar; refresh stale cycle-0088 example corpus
#163 — the construction op-script (`aura graph build`) had no durable public
schema doc, and the only worked-example corpus had drifted out of sync with the
current binary, so a consumer copying the visible example was misled.

Grammar reference (durable): a compact op-script grammar — the six verbs
(source/input/add/feed/connect/expose), their keys, the typed-Scalar bind form
({"I64":2}) and the capitalized ScalarKind, the by-identifier / dotted-port
model, and the add-`name` vs expose-`as` distinction — added to the C24 ledger
entry, with a correct worked-example pointer. Surfacing the same grammar in
`aura graph build --help` is left to ride with the CLI-discoverability work
(#159), keeping this change decoupled from the deferred World/project-as-crate
layer.

Corpus refresh: the cycle-0088 fixture corpus replayed against the current
binary failed on two accumulated drifts. (1) `add`'s naming key was renamed
`as` -> `name` (3c4b667; `expose` keeps `as` as a real alias) — the inputs now
use `name` for `add` ops only. (2) The dataflow-cycle bug the 0088 fieldtest
found was fixed (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
2026-06-30 15:45:23 +02:00
Brummel 3f75d59598 audit(0090): cycle + C24 milestone close — fieldtest green; ledger + rm ephemeral spec/plan
Closes the cycle-0090 (#156) work and delivers the "Topology-as-data:
blueprint serialization & loader (C24)" milestone.

Cycle-0090 audit (architect drift review, range 7ad7f58..HEAD): drift-clean
on substance. Tier-1 forward-tolerance is genuinely serde-default
(deny_unknown_fields = 0 in every production struct), pinned at envelope +
primitive across both the in-crate and public-seam tests; Tier-2 refusals
real and green; no struct/signature/runtime change; the invariant-5
acyclicity lockstep (construction gate <-> bootstrap Kahn sort) undisturbed;
invariant-9 resolver stays injected, no registry. The one medium item the
architect flagged — the C24 Remaining block carried no deferral signal for
#158/#159 — is fixed in this commit's ledger update.

Milestone fieldtest (fieldtests/milestone-topology-as-data/, the close-gate
evidence): GREEN, 0 behavioural bugs. A downstream consumer ran the full
author -> serialize -> load -> construct -> introspect -> reproduce story from
the public surface alone — round-trip bit-exact on both authoring surfaces
(Rust builder + op-script CLI), build-free introspection, Tier-1/Tier-2
forward-compat by name, fail-fast diagnostics by identifier. Verified the
headline claims myself (the two Rust binaries + the CLI build/diff + the
fail-fast exits), not just the agent's report.

Findings triaged to the forward queue (none blocking): op-script grammar
undocumented + a stale cycle-0088 example (#163); canonical trailing-newline
divergence + Composite Debug/PartialEq ergonomics (#164); CLI discoverability
of build/introspect + no `graph run` (commented on #159); the newline as a
content-addressing prerequisite (commented on #158).

#158 (content-address topology in the manifest) and #159 (retire the
hard-wired harnesses) were moved OUT of this milestone: both are premature
until the World generates runs from blueprint-data (commit still fully
identifies every hard-wired topology), so they belong to the
project-as-crate / World cycle. The C24 ledger Status records the delivery
and the deferral.

Ephemeral cycle-0090 spec + plan removed per the lifecycle convention.
The Gitea milestone container is closed on user ratification (this run
tees up the close, does not click it).
2026-06-30 14:43:28 +02:00
Brummel cdd2da6337 feat(0090): blueprint format forward-compat — Tier-1 tolerance pinned, two-tier discipline codified (closes #156)
The blueprint data format (C24) extends additively under one codified
two-tier discipline:
- Tier-1 (additive-optional): a new optional field/section is tolerated by an
  older reader (serde ignores unknown fields — no deny_unknown_fields), with no
  format_version bump; by C1 a new optional field defaults to prior behaviour,
  so an old blueprint reproduces the same graph.
- Tier-2 (must-understand: a new node type, edge semantics, or structural-axis
  kind): bumps format_version, so an old reader refuses cleanly
  (LoadError::UnsupportedVersion / UnknownNodeType) rather than silently
  building a different graph.

Most of #156 already shipped with #155 (cycle 0087): the envelope, omit-defaults
canonical form, and both Tier-2 load-path refusals were present and green. This
cycle pins the one previously-unproven property — Tier-1 forward-tolerance of an
unknown optional field on the current loader — and codifies the discipline.

Changes:
- New characterization pin (in-crate, blueprint_serde.rs):
  unknown_optional_field_is_tolerated_byte_identically — an unknown optional key
  at doc and primitive level loads byte-identically and runs bit-identically.
- Two public-seam E2E pins (blueprint_serde_e2e.rs, added by the E2E phase):
  unknown_envelope_field_tolerated_through_public_api and
  unknown_primitive_field_tolerated_through_public_api — split the two distinct
  tolerance surfaces (envelope vs primitive) across the World/cdylib crate
  boundary, where a deny_unknown_fields added to one would break forward-compat
  while leaving the in-crate test green.
- LoadError::UnsupportedVersion doc reworded from the deferred "is #156" note to
  the settled two-tier discipline statement.
- C24 ledger (docs/design/INDEX.md): Status codification sentence added; #156
  dropped from Remaining (#158/#159 stay).

Derived fork (recorded on #156): the per-section required-flag mechanism (PNG
critical-vs-ancillary) is NOT built — the version envelope already gives the
must-understand refusal at version granularity, no current Tier-2 section
validates a finer scheme, and C1 holds either way.

The pin is a characterization test (GREEN on first run) — it documents existing
serde silent-ignore behaviour, no production code changed. Verified: cargo test
-p aura-engine green (all targets, three new tests confirmed by name); cargo
clippy --workspace --all-targets -- -D warnings clean.
2026-06-30 13:28:25 +02:00
Brummel ead064df50 plan: 0090 blueprint forward-compat
Single-task iteration for #156: append the Tier-1 forward-tolerance
characterization test (unknown_optional_field_is_tolerated_byte_identically),
reword the LoadError::UnsupportedVersion doc to the settled two-tier
discipline, and codify it in the C24 ledger (Status sentence + drop #156 from
Remaining). No struct/signature/runtime change. refs #156
2026-06-30 13:16:21 +02:00
Brummel cb558519b0 spec: 0090 blueprint forward-compat two-tier discipline (boss-signed)
Iteration within the C24 topology-as-data milestone (#156). Codifies the
blueprint format's additive forward-compatibility under one discipline:
Tier-1 (additive-optional) = serde-default silent-ignore, no version bump,
C1 default-equals-prior-behaviour; Tier-2 (must-understand: new node type,
edge semantics, structural-axis kind) = bump format_version so an old reader
refuses cleanly rather than building a different graph.

Most of #156 already shipped with #155 (cycle 0087): the format_version
envelope, omit-defaults canonical form, and both Tier-2 load-path refusals
(LoadError::UnsupportedVersion / UnknownNodeType) are present and green.
The genuine remainder is one unproven property — forward-tolerance of an
unknown optional field on the current loader — plus codifying the discipline
in the LoadError doc and the C24 ledger entry. Not a new mechanism.

Derived fork recorded on #156: the per-section required-flag mechanism (PNG
critical-vs-ancillary) is NOT built — the version envelope already gives the
must-understand refusal at version granularity, there is no current Tier-2
section to validate a finer scheme against, and C1 holds either way.

Boss-signed on a grounding-check PASS (all current-behaviour claims ratified
by named green tests; the new Tier-1 pin correctly absent — this cycle's
deliverable). refs #156
2026-06-30 13:11:39 +02:00
Brummel 3c4b667955 feat: construction add names a node via name, mirroring the builder (#157)
The op-script authoring form is meant to be the Rust builder lifted into data —
every op is a 1:1 image of a GraphBuilder method. The one un-builder-ish key was
`add`'s `"as"`: the builder names a node with `.named("fast")`, so the data echo
is `"name"`, not the SQL-ish aliasing word `as`. Renamed the `add` wire key
`as -> name`.

`expose` keeps `as` — there it is a genuine alias (`expose bias.bias as bias`,
mirroring the builder's positional name arg). `source`/`input` keep their terse
verbs: those are plain shortenings of `source_role`/`input_role`, not a foreign
concept the way `as`-for-naming was.

CLI-side only: a one-attribute serde rename on the `OpDoc` Add DTO
(`#[serde(rename = "name")]`), since the engine `Op` is serde-free (the engine
field stays `as_name`, untouched). Updated the op-list test documents to the
`name` key (add ops only; expose's `as` left intact) and added an explicit pin
(`add_names_node_via_name_key`). The cycle-0088 fieldtest corpus
(fieldtests/cycle-0088-construction-op-script/*.json) is left on the old `as`
form as historical cycle-0088 evidence (it is not run by the suite).

Verified: full workspace suite + clippy green; `aura graph build` on a `name`-keyed
doc names the node and emits it in the #155 blueprint.
2026-06-30 12:22:45 +02:00
Brummel 7ad7f58798 audit(0089): cycle close — record acyclicity lockstep + C24 ledger refresh; rm ephemeral spec/plan
Cycle-close audit for the /boss run that followed up the cycle-0088 construction
fieldtest (#161 cycle-reject + #162 diagnostics-by-identifier). Regression gate:
full workspace suite green (0 failed).

Architect drift review (range 86841b0..HEAD): drift_found, two items, both
documentation-side — the code is correct and green.
- What holds: invariant 5 / C9 acyclicity (closes_cycle is a correct eager
  reachability gate, no false positive on a valid DAG); C24 no-second-validator
  (blueprint.rs untouched, finish() only translates the gates' returned
  CompileError); fieldtest corpus handled per convention.
- Resolved (fix path, ledger): the C24 Status now enumerates the #161 eager
  acyclicity gate and captures the LOCKSTEP the architect flagged — closes_cycle
  (construction-time) and the bootstrap Kahn sort (harness.rs, BootstrapError::Cycle)
  are now two homes of invariant-5 that must co-evolve when the explicit
  delay/register node lands; plus a note that the holistic finalize faults now read
  by-identifier (#162) while still calling the unchanged gates.

Ephemeral 0089 spec + plan git-rm'd per the active-cycle artefact lifecycle; the
durable rationale is lifted to the ledger above. No code change in this commit.
2026-06-30 11:57:25 +02:00
Brummel f1ab81d1b8 feat(0089): construction diagnostics read by-identifier (closes #162)
The cycle-0088 fieldtest found three presentation defects on the construction
op-script surface, all where the surface dropped out of its by-identifier register.
Presentation only — every fault still fires identically (same exit code, same
op/finalize attribution); only the rendered cause changes.

- Item 1 (finalize by-identifier). The holistic finalize fault leaked the raw
  index Debug form (`UnconnectedPort { node: 2, slot: 1 }`). `GraphSession::finish()`
  now translates the three reachable index-carrying CompileErrors into new
  by-identifier OpError variants (UnconnectedPort{node,slot} / RoleKindMismatch{role}
  / UnboundRootRole{role}) using the session's `ids`/`schemas` and the composite's
  `input_roles()` — the holistic gate calls (validate_wiring,
  check_param_namespace_injective, check_root_roles_bound) stay byte-for-byte
  unchanged (C24: no second validator; only the `.map_err` closures changed).
  `aura graph build` now prints `finalize: slot sub.rhs is unconnected`.
- Item 2 (bind mismatch prose). `format_op_error`'s BadParam arm matches the
  BindOpError variants instead of `{:?}`: `op 1 (add): param fast.length expects I64
  but got F64`, matching the connect mismatch register.
- Item 3 (discoverable bind form). `introspect --node` shows the typed-Scalar bind
  form: `param length:I64  (bind {"I64": <v>})`.

Item 1 spans both crates atomically (adding OpError variants forces the exhaustive
format_op_error match; the finish() translation flips behavioural test pins in both
crates). Five test touches: the two in-file unit tests, the two E2E twins plan-recon
found that the spec's testing strategy under-enumerated
(construction_e2e.rs, tests/graph_construct.rs), and a new unbound-root-role test;
the implementer added RoleKindMismatch / Ambiguous/UnknownParam coverage too. The
CLI finalize test was tightened (orchestrator) to pin `sub.rhs` and the absence of a
raw `node:` index.

Spec boss-signed on a grounding-check PASS (docs/specs+plans/0089). Verified: full
workspace suite + clippy green; the three messages reproduce exactly against the
built binary on the fieldtest fixtures.
2026-06-30 11:49:03 +02:00
Brummel c6c181f715 plan: 0089 construction diagnostics polish (refs #162)
Two tasks: item 1 (by-identifier finalize) lands atomically across both crates —
three OpError variants + finish() translation + the CLI presenter arms + five test
touches (two of them E2E twins plan-recon found that the spec's testing strategy
under-enumerated: construction_e2e.rs and tests/graph_construct.rs); items 2 & 3
(bind-mismatch prose + discoverable bind form) are additive CLI changes.
2026-06-30 11:08:38 +02:00
Brummel 365f112451 spec: 0089 construction diagnostics polish (boss-signed) (refs #162)
Three presentation-only defects the cycle-0088 fieldtest found on the
construction op-script surface: the holistic finalize fault leaks raw machine
indices (UnconnectedPort { node: 2, slot: 1 }) where the by-identifier mapping
demonstrably exists; the bind-kind mismatch leaks a Debug struct; the typed-Scalar
bind form is invisible from introspect --node. Boss-signed on a grounding-check
PASS (every load-bearing assumption ratified by a currently-green test or the
defining code).
2026-06-30 11:00:48 +02:00
Brummel 16520429be fix(0088): reject dataflow cycles in construction build (closes #161)
The construction op-script service accepted a dataflow cycle and exited 0,
emitting a non-DAG blueprint — violating domain invariant 5 / C9 (the graph is a
DAG; the only feedback path is an explicit delay/state node). The cycle-0088
fieldtest surfaced it (fieldtests/cycle-0088-construction-op-script/FINDINGS.md).

The construction gate set had no acyclicity check: a cyclic interior-edge set
passed both the eager per-op gates and the holistic finish, so replay() returned
Ok. Fix: an eager reachability check at the connect op that closes the cycle —
GraphSession::closes_cycle(from, to) asks "does the `to` node already reach the
`from` node through the edges added so far?" and rejects with a new by-identifier
OpError::WouldCycle { from, to } ("connecting from -> to would close a cycle"),
threaded through the CLI's exhaustive format_op_error. The fault is attributed to
the offending connect op, consistent with the surface's eager per-op diagnostics.

Eager rather than a holistic topological check at finish: a cycle is closed by
one specific connect, and naming that connect is the sharpest by-identifier error.

RED-first: two tests pin the symptom (a 2-cycle a<->b and a self-loop), both red
before the fix and green after; the 19 existing construction tests, the full
workspace suite, and clippy stay green.

Scope is the cycle only. The validity-floor cases the fieldtest also noted (an
unfed declared source role, an output-less graph) are degenerate-but-well-formed,
not invariant violations, and are deferred (refs #161) pending the build->consume
edge (#28) that defines what a valid emitted blueprint must guarantee.
2026-06-30 10:48:03 +02:00
Brummel 985e129129 test(fieldtest): cycle 0088 construction op-script — downstream consumer pass
Exercise the construction op-script service (#157) purely as a downstream
consumer authoring topology as data: discover-then-author an SMA-crossover
bias, author-with-`--unwired`, and 15 error-ergonomics fixtures. Public
interface only (the C24 ledger + the CLI's own output); no implementation
source read.

Confirmed working: discover→author round-trip, the `--unwired` by-identifier
authoring aid, eager per-op diagnostics. Surfaced one bug + frictions, all
reproduced and filed forward:
- #161 (bug): `graph build` accepts a dataflow cycle — DAG invariant 5
  unenforced; reproduced directly before filing.
- #162: construction error/bind ergonomics (raw-index finalize, Debug-struct
  bind-mismatch, undiscoverable typed-Scalar bind).
- #28: no CLI consumer for the emitted #155 blueprint (round-trip dead-ends).
- #159: construction surface undiscoverable from the CLI usage/help.

FINDINGS.md carries the full report + the triage table. The two 1.4 MB
self-contained viewer HTML dumps from the round-trip check are dropped as
bloat (the byte-identical result is recorded in FINDINGS).
2026-06-29 22:21:43 +02:00
Brummel 86841b0740 audit(0088): cycle close — drift fixes + C24 ledger refresh; close construction service
Architect drift review of cycle 0088 (#157, the introspectable fail-fast
construction service: §A shared gate predicates, §B the GraphSession/Op/replay
per-op surface + introspection, §C the aura graph build/introspect CLI).

What holds (confirmed against the diff): no-second-validator (C24) — edge_kind_check
is the single kind-check site called by both validate_wiring and
GraphSession::connect, and validate_wiring / check_param_namespace_injective are
reused verbatim in finish(); engine domain-free (invariant 9) — construction takes an
injected Fn(&str)->Option<PrimitiveBuilder>, no registry, the closed match +
std_vocabulary_types live in aura-std; topology-as-data / no DSL (C24/C17/inv 10) —
the engine Op is serde-free, the OpDoc wire DTO is CLI-side, ops are static and
non-Turing-complete; replay-equals-builder byte-identity (construction_e2e.rs) protects
C9/C19/C23.

Resolution:
- fix (drift-med): the root-role-boundness gate was the one holistic check finish()
  re-implemented instead of sharing — extracted check_root_roles_bound (blueprint.rs)
  and called from BOTH compile_with_cells and finish(), so finish() now reuses ALL the
  holistic gates and the no-second-validator claim is literally true. Behaviour-
  preserving (same UnboundRootRole variant; unbound_root_role_is_rejected stays green).
- fix (drift-med): removed the #![allow(dead_code)] on construction.rs — empirically
  unnecessary (the pub re-exported surface is reachable; clippy -D warnings green
  without it) — and its now-inaccurate comment (it claimed removal "when the consumer
  lands", but the consumer is a separate crate). Also tidied a stale graph_construct.rs
  module-doc line.
- fix (ledger): C24 Status refreshed — #157 moved out of "Remaining" with a cycle-0088
  realization note (the construction service: eager/holistic gate split, build-free
  introspection, emits the #155 blueprint); #159 reframed as paired with #157.
- carry-on (debt-med, forward-noted): the std_vocabulary roster is now a third hand-kept
  copy (match + test list + std_vocabulary_types) and introspect --vocabulary escalates
  #160's gap to user-facing — noted on #160; still fails safe.
- carry-on (scaling-low, forward-noted): the engine Op -> wire OpDoc direction is
  unguarded for future Op variants — noted on #156 (the format-extension track).

No architect-sweep / regression script is declared in the project facts, so the suite
is the gate: cargo test --workspace 51 suites green; clippy --all-targets -D warnings
clean. Cycle drift-clean.

Ephemeral cycle artifacts removed (lifecycle convention): docs/specs/0088-*,
docs/plans/0088-*.

closes #157
2026-06-29 21:48:40 +02:00
Brummel 25e452aaf7 feat(0088): §C construction op-script CLI — aura graph build/introspect
Iteration-2 §C of the construction service (#157), the CLI shell over the
iteration-1 engine core (ea1ca32 + 27ac4dc): a declarative, replayable JSON
op-list document drives the engine's per-op-fallible construction surface.

- graph_construct.rs (new, aura-cli): an `OpDoc` serde DTO (`#[serde(tag="op",
  rename_all="lowercase")]` + field renames type/as) deserializes the document
  and maps 1:1 into the serde-free engine `Op` — the wire format is a CLI
  concern, the engine owns no second representation. Bind values use the typed
  Scalar form (`{"I64":2}`), kinds the capitalized #155 form (`"F64"`).
- `aura graph build`: read the op-list from stdin, replay through std_vocabulary,
  emit the #155 blueprint JSON on success; on the first failing op print
  `op N (kind): cause` to stderr (the op-kind recovered from the retained parsed
  list) / `finalize: cause` for a holistic fault, and exit non-zero. A CLI-side
  format_op_error phrases each OpError (the engine error types stay Display-free).
- `aura graph introspect`: `--vocabulary` (std_vocabulary_types), `--node <T>`
  (a type's ports/kinds + param paths off the pre-build schema), `--unwired`
  (a partial document's open slots via GraphSession::unwired) — all build-free.
- Two new argv arms over the flat match; the bare `["graph"]` HTML-render arm
  (sample_blueprint, #159's concern) is left intact.

#157 acceptance now demonstrated through the CLI (E2E in tests/graph_construct.rs,
8 tests): a document builds to the #155 blueprint that compiles identical to its
Rust-built twin (C1); an invalid op is rejected at the op, named; introspection
answers vocabulary / ports+kinds / unwired slots without a build. Full suite
green (51 suites); clippy --all-targets -D warnings clean.

The JSON op-list reuses #155's Scalar/ScalarKind serde shapes; full harnesses
(SimBroker/Recorder construction-arg builders) await #156. Wire-format forks
derived + recorded on #157.

refs #157
2026-06-29 21:30:46 +02:00
Brummel f271e40d17 plan: 0088 construction op-script — iteration 2 (§C CLI shell)
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
2026-06-29 20:42:16 +02:00
Brummel 27ac4dc537 feat(0088): §B construction op-script surface — GraphSession/Op/replay + introspection
Iteration-1 §B of the construction service (#157), on the §A shared predicates
(ea1ca32): a declarative, replayable by-identifier op-script that builds a
runnable blueprint through validated ops, reusing the engine's existing gates at
two cadences — no second validator (C24).

- construction.rs: `Op` (Source/Input/Add/Feed/Connect/Expose, 1:1 with the
  document, dotted by-identifier ports), `OpError` (by-identifier causes),
  `GraphSession` (per-op-fallible accumulator) and the free `replay` driver
  (stop-at-first-failing-op, naming it by `(op_index, OpError)`).
  - Eager (per-op): name resolution + edge_kind_check + the >1-cover
    DoubleWiredPort arm + try_bind — all calling the §A shared predicates.
  - Holistic (finish): the 0-cover totality arm, param-namespace injectivity, and
    root-role boundness — the SAME unchanged engine gates
    (check_param_namespace_injective / validate_wiring), now at end-of-document.
  - Build-free introspection: `unwired()` (a partial document's still-open slots).
- aura-std std_vocabulary_types(): the enumerable companion to the closed
  std_vocabulary match (a `fn(&str)->Option<…>` resolver cannot be listed),
  lockstep-tested in the list->resolver direction; the reverse fails safe (#160).
- aura-engine exports replay/GraphSession/Op/OpError + re-exports BindOpError.

Acceptance (engine level): a sink-free price->fast/slow SMA->Sub->Bias signal
root built through the ops compiles to a FlatGraph byte-identical (edges +
sources) to its GraphBuilder twin (C1); an invalid op is rejected at the op,
named; vocabulary + unwired slots answer without a build. Pinned by
construction.rs unit tests + a construction_e2e.rs integration test over the
public seam the iteration-2 CLI will consume.

Notable judgement calls folded in from the implement loop: feed is all-or-nothing
(two-phase resolve-then-commit, no partial wiring on a mid-fan-out fault); the
node identifier is stamped onto the builder (`named(&id)`) so two same-type nodes
get distinct param paths and do not trip the injectivity gate prematurely; a
module-level `#![allow(dead_code)]` covers the public surface until the
iteration-2 CLI consumes it. Full suite green; clippy --all-targets -D warnings
clean.

Iteration 2 (the JSON op-list encoding + `aura graph build`/`introspect` CLI)
remains; #157 stays open. refs #157
2026-06-29 20:28:41 +02:00
Brummel ea1ca32dbd feat(0088): §A shared gate predicates — edge_kind_check, resolution helpers, try_bind
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
2026-06-29 18:38:34 +02:00
Brummel a5b887a955 plan: 0088 construction op-script — iteration 1 (engine core)
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
2026-06-29 18:08:27 +02:00
Brummel 114fa3d718 spec: 0088 construction op-script (boss-signed)
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
2026-06-29 17:55:46 +02:00
Brummel 468fbae62b audit(0087): cycle close — drift-clean after C24/C9 realization note; rm ephemeral spec/plan
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-*.
2026-06-29 15:01:11 +02:00
Brummel d5602ec5ad feat(0087): blueprint serialization & loader — close the graph-as-data loop
C24 first cut (#155): a blueprint — the param-generic, named graph-as-data a
Rust builder produces — is now a first-class serializable data value with a
canonical, versioned format, and the engine has the missing load path
(data -> blueprint -> FlatGraph), not only the Rust-builder construction path.

What ships:
- `blueprint_to_json` / `blueprint_from_json` in a new `aura-engine`
  blueprint_serde module: a faithful serde DTO projection (the build closures
  dropped; re-derived on load), top-level `format_version` envelope, canonical
  compact JSON (struct field order, defaults omitted via skip_serializing_if).
- A resolver seam: the loader is generic over an injected
  `Fn(&str) -> Option<PrimitiveBuilder>`; the concrete closed `match` over the
  aura-std vocabulary lives in aura-std (`std_vocabulary`), never in the engine
  (the engine stays domain-free: lib deps aura-core + aura-analysis only, never
  the node-vocabulary crate). This is C24's compiled-in closed-set referenced by
  type identity, NOT a dynamic/marketplace node registry (domain invariant 9).
- serde derives on the serialized plain types (Edge, Target, OutField, Role,
  BoundParam, ScalarKind); BoundParam additionally re-exported from aura-core's
  root (an additive fix the plan's file list missed).

Load-bearing scope decisions (derived, logged on #155):
- Sinks are excluded from the serialized blueprint — a recording sink captures an
  mpsc::Sender (runtime identity, not param/topology, C19 value-empty); sink
  addressing is the C18-registry / #101 concern. The round-trip test attaches
  identical sinks post-load to both twins.
- Construction-arg builders (LinComb(arity), CostSum(n), SimBroker(pip),
  Session(..)) are out of the round-trippable set: their structural args are a
  C20 structural-axis concern the param-generic format does not yet encode; an
  absent type_id fails the load cleanly (UnknownNodeType), never a silent wrong
  graph. #155's vocabulary is the 22 zero-arg aura-std builders. Additively
  extensible later (#156).

Orchestrator hardening on review: the serializer now emits bound params in
ascending original-slot order (mirroring the loader), so the canonical form is
bind-order-independent — a precondition for content-addressing (#158). Identity
today (every #155 node has <=1 param); holds by construction for future
multi-param nodes.

Acceptance (all green): a serialized blueprint runs bit-identical (C1) to its
Rust-built twin; serializer/loader are inverse (canonical idempotence, incl. a
recursive nested composite); unknown-type and unsupported-version fail named,
never panicking. cargo test --workspace 748 passed; clippy --all-targets
-D warnings clean.

closes #155
2026-06-29 14:54:45 +02:00
Brummel b2278538ab plan: 0087 blueprint serialization & loader
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
2026-06-29 13:45:31 +02:00
Brummel ce86b4ec30 spec: 0087 blueprint serialization & loader (boss-signed)
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
2026-06-29 13:25:57 +02:00
Brummel f80571a46a docs(ledger): C24 — blueprint as serializable World-owned data; resolve #109
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
2026-06-29 12:33:38 +02:00
Brummel 893698a1e6 audit(0086): cycle close — drift-clean; rm ephemeral spec/plan
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.
2026-06-29 12:16:21 +02:00
Brummel d925a747cb feat(0086): cost-flag CLI ergonomics — units note, named diagnostics, non-R-harness guard
#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
2026-06-29 12:12:22 +02:00
Brummel dfc01bf837 plan: 0086 cost-flag CLI ergonomics
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
2026-06-29 11:35:03 +02:00
Brummel f26b33f345 spec: 0086 cost-flag CLI ergonomics (boss-signed)
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
2026-06-29 11:24:55 +02:00
Brummel dcf58c5d26 docs(project-layout): retire exposure/realistic-broker framing → bias/R/cost-model
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
2026-06-29 11:17:39 +02:00
Brummel d8946b1d7b docs(ledger): correct stale realization notes — C12 sharing, aura-std, C15 SessionNode
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).
2026-06-29 11:17:39 +02:00
Brummel 8d04a845ee docs(glossary): align terminology with industry standard
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.
2026-06-29 10:55:37 +02:00
Brummel dff99c93e4 fieldtest(milestone): cost-model graph (#148) — capability delivered green
Milestone-close gate for "Cost-model graph (in R)" (#148). The fieldtester exercised
the milestone capability end-to-end from the public CLI only (no implementation
source), on the stage1-r harness, against ledger C10 + the glossary.

Roll-up: friction_found — 0 capability bugs; the milestone promise is empirically
delivered:
- bare gross-R run is clean (net == gross, no cost forced);
- per-trade cost: gross pinned, net drops strictly/monotonically, net_r_equity tap
  persists;
- the capstone per-held-cycle carry BLEEDS the net-R curve continuously over a hold
  (the gross-net gap grows 0.367 -> 0.734 -> 1.101 -> 1.468 across one hold, vs the
  per-trade run's flat 0.367) — the holding-cost shape change, observable end-to-end;
- composition is exact-additive (the composed drag equals the sum of the single-cost
  drags to the last ULP) and `aura chart` renders the real net_r_equity series.

Evidence under fieldtests/milestone-cost-model-graph/ (run_scenarios.sh +
captured_outputs.txt + traces/ + the charted bleed + FINDINGS.md).

Non-blocking findings filed forward (none gates the close):
- cost-flag CLI ergonomics — units hint, negative-rate diagnostic, non-R-harness
  no-op + a C10 spec_gap -> #153;
- docs/project-layout.md describes the retired `aura backtest --broker` path and omits
  the cost flags -> folded into #151.

refs #148
2026-06-29 02:35:24 +02:00
Brummel 84ec2ab5d1 audit(0085): cycle close — drift-clean; C10 cycle-0085 realization note
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
2026-06-29 01:06:50 +02:00
Brummel f5e00a9c72 feat(0085): per-cycle-held accrual — CarryCost + ChargeMode (approach B)
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". A carry/funding cost
is charged for every cycle a position is held, so the net_r_equity equity curve bleeds
continuously over the hold (approach B, "ja, mach B") rather than stepping at close.

Mechanism (logged on #148): ChargeMode { AtClose, PerHeldCycle } is a property of the
cost factor, read by the one shared CostRunner (no second runner type). The AtClose arm
is the pre-cycle-5 eval tokens VERBATIM (IEEE-754 byte-identity). The PerHeldCycle arm
accrues `per` into `acc` each held cycle, dumps the accrued total into `cum` at close
(resetting acc), and marks the open position via a growing open_cost_in_r. The bleed
lives in open_cost_in_r, which the net_r_equity tap already subtracts — so summarize_r
and the CLI net_eq wiring are UNCHANGED, and the cycle-0083/0084 net_expectancy_r
goldens (-614.3134020253314 flat, -615.0304388396047 composed) + the C18 no-cost golden
stay byte-identical.

CarryCost is a ConstantCost twin differing only in charge_mode() -> PerHeldCycle (a
labelled stress parameter, the flat base of the accrual family). Wired through the
existing cost_graph/CostSum aggregation; a per-trade ConstantCost and a per-held-cycle
CarryCost compose in one run (the costs sum per-field — the composed golden is exactly
the sum of the two single-cost charges). New run-path --carry-per-cycle flag
(sweep/walkforward/mc pass None, as the existing cost flags do).

Tests: 2 RED-first unit B-proofs (open_cost grows over the hold, dumps the total at
close, acc resets between trades — the discriminator a scalar net_expectancy_r cannot
see); the CarryCost twin's 5 unit tests; 2 captured net_expectancy_r goldens
(-768.2095026600887 carry, -1383.7939051991182 composed); a net_r_equity-bleeds-over-
the-hold integration test (the terminal open hold's r_equity-net_r_equity gap strictly
grows); plus negative-rate-refused-exit-2, monotone-in-rate, and zero-rate-exactly-free
guards.

Verified: cargo test --workspace (0 failures), cargo build --workspace, cargo clippy
--workspace --all-targets -- -D warnings clean. summarize_r / aura-analysis untouched.
Deferred to later #148 cycles (logged there): notional-based carry, the calendar-aware
overnight swap, sweep-path cost.

refs #148
2026-06-29 01:00:51 +02:00
Brummel 2a8ee86489 plan: 0085 per-cycle-held accrual + CarryCost
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
2026-06-29 00:19:48 +02:00
Brummel 19ba1ec978 spec: 0085 per-cycle-held accrual + CarryCost (boss-signed)
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
2026-06-29 00:02:26 +02:00
Brummel 39f9387c57 docs(glossary): add 'tap' — named recorded sink stream
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.
2026-06-28 22:24:42 +02:00
Brummel face1879a6 audit(0084): cycle close — drift-clean (code); C10 cycle-0084 note + doc fixes
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
2026-06-28 22:06:06 +02:00
Brummel d5c44dd1ea feat(0084): cost-graph composite-builder
Cycle 4 of milestone #148 (Cost-model graph in R), decision E.

New `cost_graph(Vec<PrimitiveBuilder>) -> Composite` in aura-composites: it fans
the 4 PM-geometry inputs to N cost nodes, surfaces each node's extra inputs
(discovered via schema introspection past GEOMETRY_WIDTH) as `cost[k].<port>`
roles, sums them through 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. GEOMETRY_WIDTH re-exported from
aura-std (first cross-crate consumer). Runtime port names .leak()'d (the
risk_executor precedent), a bounded one-time cost at blueprint construction.

Behaviour-preserving at the value level: the composite inlines at bootstrap (C11)
to the same flat fan-in, so the two cycle-3 net_expectancy_r goldens
(-614.3134020253314 flat, -615.0304388396047 composed), the C18 no-cost golden,
and the full suite stay green verbatim. Honours C9 (a composite of cost nodes is
ordinary downstream nodes), C16 (wiring lives in aura-composites, not aura-engine),
C23 (label drift under cost_graph nesting is permitted; no with-cost graph
label/shape golden exists).

Four aura-composites unit tests pin the exposed contract: the two planned (n=2
heterogeneous role set; n=1 no-extra shape) plus two the implementer added that
strengthen the headline — arbitrary-arity per-node namespacing (n=3, two
VolSlippageCost -> distinct cost[1]/cost[2].volatility, the property that retires
the 2-node cap) and geometry fan-once (n=2 extra-free -> only the 4 geometry roles).

Accepted nit (held, non-gating): the CLI reconstructs the `cost[k].volatility`
role name to feed the vol node's extra input — a string coupling to the composite's
public role-naming convention. Kept: role-based composite wiring addresses roles by
name (as the CLI already does for "closed"/"bias"/"price"); the convention is tested
+ build-validated; a decoupling accessor would widen this cycle's scope.

Verified: cargo test --workspace (0 failures), clippy --workspace --all-targets
-D warnings clean, cargo doc -p aura-composites clean.

refs #148
2026-06-28 21:52:06 +02:00
Brummel 550d5ce947 plan: 0084 cost-graph composite-builder
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
2026-06-28 21:22:26 +02:00
Brummel 9fb3d8b8ee spec: 0084 cost-graph composite-builder (boss-signed)
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
2026-06-28 21:10:07 +02:00
Brummel fc52b4fced audit(0083): cycle close — drift-clean (code); C10 cycle-0083 note + lib.rs doc
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
2026-06-28 19:32:07 +02:00
Brummel 6b53c239dd feat(0083): CostNode trait + shared cost-record contract
Lift the duplicated cost-node skeleton — shared verbatim by ConstantCost and
VolSlippageCost and locked by convention against CostSum — into one abstraction:

- A new aura-std/src/cost.rs owns the cost-record contract (COST_WIDTH,
  COST_FIELD_NAMES, GEOMETRY_WIDTH), the CostNode factor trait (one hook:
  cost_numerator, in price units), the generic CostRunner<F> adapter that holds
  the shared state (cum, out) and the co-temporality skeleton (geometry gating,
  numerator/latched R-normalization, closed/open charge, 3-field emit), and a
  cost_node_builder schema assembler.
- ConstantCost and VolSlippageCost become thin CostNode factors; their new()
  returns CostRunner<Self>, so every existing call site and unit test binds
  transparently and stays green verbatim.
- CostSum and main.rs drop their local triple consts for the shared
  COST_FIELD_NAMES — the cycle-2 audit-flagged by-convention 3-field lockstep is
  now a structural single-source contract (the four copies of the triple collapse
  to one). main.rs also reads COST_WIDTH in place of the literal slot stride.

Behaviour-preserving: the builders emit unchanged schemas (same port names), so
the wiring, the net_r_equity seam, and summarize_r are untouched; the
numerator/latched token form is preserved verbatim (IEEE-754 byte-identity). The
existing suite is the regression net — all green: the per-node unit tests pass
verbatim (0.5/1.5, 0.375/1.375), the composition E2E exact, the C18 no-cost
golden byte-identical. Two new CLI characterization goldens pin the exact
net_expectancy_r of the flat and composed cost paths (the prior tests only
asserted net < gross, which a numerator drift would pass silently). New cost.rs
runner/contract tests + the HalfSpreadCost author doctest cover the new surface.

Deviation from the plan: CostNode::name() was dropped (the plan over-specified
it) — it is dead surface, nothing consumes it (CostRunner forwards label();
cost_node_builder takes the name as an explicit arg). Removed from the trait,
both impls, the doctest, and the test stubs; build + suite + clippy green.

Verified: cargo build --workspace --all-targets clean; cargo test --workspace
0 failures; cargo clippy --workspace --all-targets -- -D warnings clean; doctest
green.

refs #148
2026-06-28 19:26:34 +02:00
Brummel c787488e64 plan: 0083 cost-node trait + shared cost-record contract
6 tasks: (1) new cost.rs — CostNode trait + CostRunner<F> + cost_node_builder +
shared COST_FIELD_NAMES/COST_WIDTH/GEOMETRY_WIDTH + runner tests + author doctest;
(2-3) migrate ConstantCost/VolSlippageCost to thin factors; (4) CostSum reads the
shared contract; (5) main.rs reads aura_std::COST_FIELD_NAMES; (6) workspace gate.
Behaviour-preserving — the existing suite is the regression net.

refs #148
2026-06-28 18:21:12 +02:00
Brummel 16f4cbb548 spec: 0083 cost-node trait + shared cost-record contract (boss-signed)
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
2026-06-28 18:11:55 +02:00