feat(engine, market, strategy, backtest): NodeSchema.doc threading across the domain crates

C29 compile/unit seam, tasks 4+5 of the self-description plan.

aura-engine (task 4): derive_signature stamps doc: "" with the stance
recorded in-code -- a derived composite signature is graph wiring, not
a vocabulary entry; no seam walks its doc, the described surface is the
composite's own doc at the register seam. All in-crate test literals
thread doc: "test-only schema".

Domain crates (task 5): the 12 production builder sites in
aura-market / aura-strategy / aura-backtest carry authored meaning
lines; test sites in aura-backtest / aura-composites / aura-ingest
thread the test-only doc.

Three texts were corrected against the actual node semantics after
quality review rather than kept from the first authoring pass:
SimBroker is the frictionless integrator of held exposure times price
return into cumulative pip equity (no fills/stops/lifecycle -- that is
PositionManagement's domain), the shared cost-node line names the
PM-geometry inputs and both charge modes (AtClose / PerHeldCycle)
instead of a "per-trade gross R" mapping, and LongOnly's line
conditions on its enabled param and speaks port-term "exposure".

Gates: cargo test green for all six touched crates (engine, market,
strategy, backtest, composites, ingest); the workspace-wide gate
follows task 6 (aura-runner is the one remaining unthreaded site,
E0063 by design until then).

refs #316
This commit is contained in:
2026-07-23 16:08:26 +02:00
parent a32dc38d18
commit d8c6938027
19 changed files with 65 additions and 7 deletions
+1
View File
@@ -1214,6 +1214,7 @@ mod tests {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "in".into() }],
output: vec![],
params: vec![],
doc: "test-only schema",
}
}
@@ -87,7 +87,12 @@ impl PositionManagement {
.collect();
PrimitiveBuilder::new(
"PositionManagement",
NodeSchema { inputs, output, params: vec![] },
NodeSchema {
inputs,
output,
params: vec![],
doc: "turns bias plus protective stop into a managed position in R",
},
|_| Box::new(PositionManagement::new()),
)
}
+1
View File
@@ -68,6 +68,7 @@ impl SimBroker {
],
output: vec![FieldSpec { name: "equity".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "frictionless sim-optimal broker: integrates held exposure times price return into cumulative pip equity",
},
move |_| Box::new(SimBroker::new(pip_size)),
)
@@ -35,6 +35,7 @@ impl ConstLongBias {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "price".into() }],
output: vec![FieldSpec { name: "bias".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
},
|_| Box::new(ConstLongBias { out: [Cell::from_f64(0.0)] }),
)
+23 -5
View File
@@ -129,7 +129,15 @@ fn derive_signature(c: &Composite) -> NodeSchema {
.collect();
let mut params = Vec::new();
collect_params(c.nodes(), c.gangs(), "", &mut params);
NodeSchema { inputs, output, params }
NodeSchema {
inputs,
output,
params,
// A derived composite signature is graph wiring, not a vocabulary entry:
// no seam walks this doc. The described surface is the composite's own
// `doc`, gated at register (C29, register seam).
doc: "",
}
}
/// The scalar kind of the interior input slot a composite target addresses,
@@ -1838,6 +1846,7 @@ mod tests {
inputs: vec![],
output: vec![FieldSpec { name: "v".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
};
let to = NodeSchema {
inputs: vec![
@@ -1846,6 +1855,7 @@ mod tests {
],
output: vec![],
params: vec![],
doc: "test-only schema",
};
assert!(edge_kind_check(&from, 0, &to, 0).is_ok());
assert_eq!(
@@ -2269,7 +2279,7 @@ mod tests {
fn pass1() -> BlueprintNode {
PrimitiveBuilder::new(
"Pass1",
NodeSchema { inputs: vec![f64_any()], output: out_v(), params: vec![] },
NodeSchema { inputs: vec![f64_any()], output: out_v(), params: vec![], doc: "test-only schema" },
|_| Box::new(Pass1 { out: [Cell::from_f64(0.0)] }),
)
.into()
@@ -2277,7 +2287,12 @@ mod tests {
fn join2() -> BlueprintNode {
PrimitiveBuilder::new(
"Join2",
NodeSchema { inputs: vec![f64_any(), f64_any()], output: out_v(), params: vec![] },
NodeSchema {
inputs: vec![f64_any(), f64_any()],
output: out_v(),
params: vec![],
doc: "test-only schema",
},
|_| Box::new(Join2 { out: [Cell::from_f64(0.0)] }),
)
.into()
@@ -2285,7 +2300,7 @@ mod tests {
fn sink_f64() -> BlueprintNode {
PrimitiveBuilder::new(
"SinkF64",
NodeSchema { inputs: vec![f64_any()], output: vec![], params: vec![] },
NodeSchema { inputs: vec![f64_any()], output: vec![], params: vec![], doc: "test-only schema" },
|_| Box::new(SinkF64),
)
.into()
@@ -2297,6 +2312,7 @@ mod tests {
inputs: vec![PortSpec { kind: ScalarKind::I64, firing: Firing::Any, name: "in".into() }],
output: vec![],
params: vec![],
doc: "test-only schema",
},
|_| Box::new(SinkI64),
)
@@ -2849,6 +2865,7 @@ mod tests {
inputs: vec![f64_any()],
output: vec![],
params: vec![],
doc: "test-only schema",
};
let h = Harness::bootstrap(FlatGraph {
nodes: vec![
@@ -3454,6 +3471,7 @@ mod tests {
inputs: vec![PortSpec { kind: ScalarKind::I64, firing: Firing::Any, name: "in".into() }],
output: vec![FieldSpec { name: "v".into(), kind: ScalarKind::I64 }],
params: vec![],
doc: "test-only schema",
},
|_| panic!("build must not run when validation fails pre-build"),
);
@@ -3556,7 +3574,7 @@ mod tests {
// cannot take .named).
let paramless_sma = PrimitiveBuilder::new(
"Pass1",
NodeSchema { inputs: vec![f64_any()], output: out_v(), params: vec![] },
NodeSchema { inputs: vec![f64_any()], output: out_v(), params: vec![], doc: "test-only schema" },
|_| Box::new(Pass1 { out: [Cell::from_f64(0.0)] }),
)
.named("sma");
+2
View File
@@ -348,6 +348,7 @@ mod tests {
],
output: vec![FieldSpec { name: "diff".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
},
|_| Box::new(Bare),
)
@@ -462,6 +463,7 @@ mod tests {
FieldSpec { name: "lo".into(), kind: ScalarKind::I64 },
],
params: vec![],
doc: "test-only schema",
},
|_| Box::new(Bare),
)
+9
View File
@@ -878,6 +878,7 @@ mod tests {
inputs: kinds.iter().enumerate().map(|(i, &kind)| PortSpec { kind, firing, name: format!("col[{i}]") }).collect(),
output: vec![],
params: vec![],
doc: "test-only schema",
}
}
@@ -895,6 +896,7 @@ mod tests {
inputs: vec![f64_port(Firing::Any), f64_port(Firing::Any)],
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -924,6 +926,7 @@ mod tests {
inputs: vec![f64_port(Firing::Barrier(0)), f64_port(Firing::Barrier(0))],
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -951,6 +954,7 @@ mod tests {
inputs: vec![f64_port(Firing::Any)],
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::Bool }],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -984,6 +988,7 @@ mod tests {
],
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -1022,6 +1027,7 @@ mod tests {
FieldSpec { name: "volume".into(), kind: ScalarKind::F64 },
],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -1057,6 +1063,7 @@ mod tests {
FieldSpec { name: "i".into(), kind: ScalarKind::I64 },
],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -1084,6 +1091,7 @@ mod tests {
inputs: vec![f64_port(Firing::Any)],
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }],
params: vec![],
doc: "test-only schema",
}
}
}
@@ -2442,6 +2450,7 @@ mod tests {
FieldSpec { name: "i".into(), kind: ScalarKind::I64 },
],
params: vec![],
doc: "test-only schema",
}
}
}
+1
View File
@@ -43,6 +43,7 @@ fn recorder_sig(kinds: &[ScalarKind]) -> NodeSchema {
.collect(),
output: vec![],
params: vec![],
doc: "test-only schema",
}
}
+1
View File
@@ -29,6 +29,7 @@ fn run_sample_over(prices: Vec<(Timestamp, Scalar)>) -> RunReport {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "in".into() }],
output: vec![],
params: vec![],
doc: "test-only schema",
};
let mut h = Harness::bootstrap(FlatGraph {
nodes: vec![
@@ -35,6 +35,7 @@ fn run_sample(window: (Timestamp, Timestamp), source: Box<dyn Source>) -> RunRep
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "in".into() }],
output: vec![],
params: vec![],
doc: "test-only schema",
};
let mut h = Harness::bootstrap(FlatGraph {
nodes: vec![
+1
View File
@@ -88,6 +88,7 @@ impl Resample {
FieldSpec { name: "close".into(), kind: ScalarKind::F64 },
],
params: vec![ParamSpec { name: "period_minutes".into(), kind: ScalarKind::I64 }],
doc: "emits a completed bar once per resample boundary, never mid-bar",
},
|p| Box::new(Resample::new(p[0].i64())),
)
+2
View File
@@ -72,6 +72,7 @@ impl Session {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "trigger".into() }],
output: vec![FieldSpec { name: "bars_since_open".into(), kind: ScalarKind::I64 }],
params: vec![],
doc: "bars elapsed since the session open, from the configured open time and timezone",
},
move |_| Box::new(Session::new(open_hour, open_minute, tz, period_minutes)),
)
@@ -99,6 +100,7 @@ impl SessionFrankfurt {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "trigger".into() }],
output: vec![FieldSpec { name: "bars_since_open".into(), kind: ScalarKind::I64 }],
params: vec![ParamSpec { name: "period_minutes".into(), kind: ScalarKind::I64 }],
doc: "bars elapsed since the Frankfurt session open (09:00 Europe/Berlin)",
},
|p| Box::new(Session::new(9, 0, chrono_tz::Europe::Berlin, p[0].i64())),
)
+1
View File
@@ -32,6 +32,7 @@ impl Bias {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "signal".into() }],
output: vec![FieldSpec { name: "bias".into(), kind: ScalarKind::F64 }],
params: vec![ParamSpec { name: "scale".into(), kind: ScalarKind::F64 }],
doc: "clamps the strategy signal into the bias contract [-1,+1]",
},
|p| Box::new(Bias::new(p[0].f64())),
)
+10 -1
View File
@@ -224,7 +224,16 @@ pub fn cost_node_builder(
) -> PrimitiveBuilder {
let mut inputs = geometry_input_ports();
inputs.extend(extra_inputs);
PrimitiveBuilder::new(name, NodeSchema { inputs, output: cost_output_fields(), params }, build)
PrimitiveBuilder::new(
name,
NodeSchema {
inputs,
output: cost_output_fields(),
params,
doc: "cost-model node: charges its cost in R from position geometry, at close or accrued per held cycle",
},
build,
)
}
#[cfg(test)]
+1
View File
@@ -58,6 +58,7 @@ impl CostSum {
.map(|n| FieldSpec { name: (*n).into(), kind: ScalarKind::F64 })
.collect(),
params: vec![],
doc: "sums cost-model contributions into one cost-in-R stream",
},
move |_| Box::new(CostSum::new(n_costs)),
)
+1
View File
@@ -31,6 +31,7 @@ impl LongOnly {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "exposure".into() }],
output: vec![FieldSpec { name: "exposure".into(), kind: ScalarKind::F64 }],
params: vec![ParamSpec { name: "enabled".into(), kind: ScalarKind::Bool }],
doc: "when enabled, clamps negative exposure to flat (long-only); otherwise passes exposure through",
},
|p| Box::new(LongOnly::new(p[0].bool())),
)
+1
View File
@@ -36,6 +36,7 @@ impl Sizer {
],
output: vec![FieldSpec { name: "size".into(), kind: ScalarKind::F64 }],
params: vec![ParamSpec { name: "risk_budget".into(), kind: ScalarKind::F64 }],
doc: "size = risk_budget / stop_distance at the deploy edge; bias only gates firing, research runs flat-1R",
},
|p| Box::new(Sizer::new(p[0].f64())),
)
+1
View File
@@ -34,6 +34,7 @@ impl FixedStop {
inputs: vec![PortSpec { kind: ScalarKind::F64, firing: Firing::Any, name: "price".into() }],
output: vec![FieldSpec { name: "stop_distance".into(), kind: ScalarKind::F64 }],
params: vec![ParamSpec { name: "distance".into(), kind: ScalarKind::F64 }],
doc: "derives the protective stop that defines the risk unit R",
},
|p| Box::new(FixedStop::new(p[0].f64())),
)
+1
View File
@@ -72,6 +72,7 @@ impl VolTfStop {
ParamSpec { name: "length".into(), kind: ScalarKind::I64 },
ParamSpec { name: "k".into(), kind: ScalarKind::F64 },
],
doc: "volatility- and timeframe-scaled protective stop rule",
},
|p| Box::new(VolTfStop::new(p[0].i64(), p[1].i64(), p[2].f64())),
)