test(cli),docs: risk-axis introspection pins + guide/glossary coverage (#216 slice 2)
closes #216 The remaining introspection e2e pins: the bare-{} envelope probe (the issue's exact reproduction) lists the optional risk slot; a bound (non-empty) risk list closes it (no open slots); an EXPLICIT empty 'risk': [] still counts as open (pins the non-emptiness branch, which an is_none()-based probe would get wrong); the --block std::risk assert is sharpened to pin the optional rendering and the block doc line (the old bare contains("risk") matched std::risk trivially). Docs: the authoring guide's bare-{} transcript gains the risk line, the §3 worked example gains a two-regime risk section (heading updated), a prose paragraph explains the structural risk axis (stop defines R; absent = the same default vol regime the verbs bind), and both validate transcripts show the new matrix counts. The glossary gains the 'risk regime' entry (canonical term; avoid: stop regime, risk section). Verified: cargo test --workspace 62 result groups / 0 failures; clippy -D warnings clean; cargo doc 0 warnings; the guide's transcript strings grep-match the shipped hint/summary bytes (1/2/1).
This commit is contained in:
@@ -615,7 +615,14 @@ fn campaign_introspect_block_risk_describes_the_regime_shape() {
|
||||
out.contains("list of stop regimes { vol: { length, k } }; absent = one default regime"),
|
||||
"the block description must name the regime shape: {out}"
|
||||
);
|
||||
assert!(out.contains("risk"), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains("optional, list of stop regimes { vol: { length, k } }; absent = one default regime"),
|
||||
"the risk slot must render as optional: {out}"
|
||||
);
|
||||
assert!(
|
||||
out.contains("std::risk — campaign section: the structural risk axis"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// #216: a complete-but-risk-less campaign document (the exact shape a
|
||||
@@ -639,6 +646,63 @@ fn campaign_introspect_unwired_lists_the_risk_slot_when_absent() {
|
||||
);
|
||||
}
|
||||
|
||||
/// #216: the bare-envelope probe (`{}` — the issue's exact reproduction)
|
||||
/// names the optional risk axis alongside the required envelope slots.
|
||||
#[test]
|
||||
fn campaign_introspect_unwired_lists_the_optional_risk_slot_on_bare_envelope() {
|
||||
let dir = temp_cwd("campaign-introspect-unwired-risk-bare");
|
||||
write_doc(&dir, "bare.json", "{}");
|
||||
let (out, code) = run_code_in(&dir, &["campaign", "introspect", "--unwired", "bare.json"]);
|
||||
assert_eq!(code, Some(0));
|
||||
assert!(
|
||||
out.contains("open slot: risk (optional, list of stop regimes { vol: { length, k } }; absent = one default regime)"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// #216: a bound (non-empty) risk list closes the slot — a complete document
|
||||
/// with risk reports no open slots at all.
|
||||
#[test]
|
||||
fn campaign_introspect_unwired_omits_a_bound_risk_slot() {
|
||||
let dir = temp_cwd("campaign-introspect-unwired-risk-bound");
|
||||
let with_risk = CAMPAIGN_DOC.replacen(
|
||||
"\"seed\": 1,",
|
||||
"\"seed\": 1,\n \"risk\": [ { \"vol\": { \"length\": 3, \"k\": 2.0 } } ],",
|
||||
1,
|
||||
);
|
||||
assert_ne!(with_risk, CAMPAIGN_DOC, "replacen must actually match the fixture's seed field");
|
||||
write_doc(&dir, "bound.campaign.json", &with_risk);
|
||||
let (out, code) =
|
||||
run_code_in(&dir, &["campaign", "introspect", "--unwired", "bound.campaign.json"]);
|
||||
assert_eq!(code, Some(0));
|
||||
assert!(!out.contains("open slot: risk"), "bound risk must not list: {out}");
|
||||
assert!(out.contains("no open slots"), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
/// #216: an EXPLICIT `"risk": []` (key present, array empty) is still an open
|
||||
/// slot — same as the key being absent entirely. The probe distinguishes
|
||||
/// "bound" from "unbound" by non-emptiness, not by key presence
|
||||
/// (`v.get("risk").and_then(|r| r.as_array()).is_some_and(|a| !a.is_empty())`);
|
||||
/// a probe that instead branched on `is_none()` would wrongly treat an
|
||||
/// explicit empty list as closed, and no other test in this file exercises
|
||||
/// that branch (the absent-key and non-empty-array cases are covered above).
|
||||
#[test]
|
||||
fn campaign_introspect_unwired_lists_the_risk_slot_when_explicitly_empty() {
|
||||
let dir = temp_cwd("campaign-introspect-unwired-risk-explicit-empty");
|
||||
let explicit_empty = CAMPAIGN_DOC.replacen("\"seed\": 1,", "\"seed\": 1,\n \"risk\": [],", 1);
|
||||
assert_ne!(explicit_empty, CAMPAIGN_DOC, "replacen must actually match the fixture's seed field");
|
||||
write_doc(&dir, "empty-risk.campaign.json", &explicit_empty);
|
||||
let (out, code) =
|
||||
run_code_in(&dir, &["campaign", "introspect", "--unwired", "empty-risk.campaign.json"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains(
|
||||
"open slot: risk (optional, list of stop regimes { vol: { length, k } }; absent = one default regime)"
|
||||
),
|
||||
"an explicit empty risk array must still count as an open slot: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn campaign_introspect_unwired_reports_the_spec_example_slots() {
|
||||
let dir = temp_cwd("campaign-introspect-unwired");
|
||||
|
||||
+14
-3
@@ -266,13 +266,14 @@ open slot: format_version (required, must be 1)
|
||||
open slot: kind (required, must be "campaign")
|
||||
open slot: name (required, string)
|
||||
open slot: data (required section: instruments + windows)
|
||||
open slot: risk (optional, list of stop regimes { vol: { length, k } }; absent = one default regime)
|
||||
open slot: strategies (required, non-empty list of { ref, axes })
|
||||
open slot: process.ref (required, content id of a process document)
|
||||
open slot: seed (required, non-negative integer)
|
||||
open slot: presentation (required section: persist_taps (equity | exposure | r_equity | net_r_equity) + emit)
|
||||
```
|
||||
|
||||
### Worked example: two instruments, one strategy, four axis points
|
||||
### Worked example: two instruments, one strategy, four axis points, two stop regimes
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -284,6 +285,10 @@ open slot: presentation (required section: persist_taps (equity | exposure | r_e
|
||||
"instruments": ["GER40", "FRA40"],
|
||||
"windows": [ { "from_ms": 1725148800000, "to_ms": 1727740800000 } ]
|
||||
},
|
||||
"risk": [
|
||||
{ "vol": { "length": 3, "k": 1.5 } },
|
||||
{ "vol": { "length": 3, "k": 3.0 } }
|
||||
],
|
||||
"strategies": [
|
||||
{
|
||||
"ref": { "content_id": "597d719b7ac607158cda3e68cd497387620397a5e93087e23da512876dafba9a" },
|
||||
@@ -306,11 +311,17 @@ Each axis name (`fast.length`, …) must name an open param of the referenced
|
||||
blueprint (`aura graph introspect --params`, §1) and declare that param's
|
||||
`ScalarKind`.
|
||||
|
||||
The optional `risk` list is the campaign's structural risk axis: every cell
|
||||
runs under every listed stop regime, so cells differ by execution discipline,
|
||||
never by signal — the regime's stop defines the risk unit R. Absent or empty,
|
||||
the matrix runs one implicit default regime (the same vol regime the
|
||||
orchestration verbs bind when their stop flags are omitted).
|
||||
|
||||
### Validate — three tiers, honest degradation
|
||||
|
||||
```
|
||||
$ aura campaign validate mra_3_campaign_full_v2.json # outside any project
|
||||
campaign document valid (intrinsic): 1 strategy(ies), 3 axes (4 points), 1 window(s)
|
||||
campaign document valid (intrinsic): 1 strategy(ies), 3 axes (4 points), 2 instrument(s), 1 window(s), 2 regime(s) — 4 cell(s)
|
||||
referential checks skipped (no Aura.toml found up from /home/…)
|
||||
```
|
||||
|
||||
@@ -319,7 +330,7 @@ registered, the same command runs two further tiers:
|
||||
|
||||
```
|
||||
$ aura campaign validate mra_3_campaign_full_v2.json # inside a project, refs registered
|
||||
campaign document valid (intrinsic): 1 strategy(ies), 3 axes (4 points), 1 window(s)
|
||||
campaign document valid (intrinsic): 1 strategy(ies), 3 axes (4 points), 2 instrument(s), 1 window(s), 2 regime(s) — 4 cell(s)
|
||||
campaign document valid (referential): all references resolve, axes are in the param space
|
||||
campaign document valid (executable): pipeline shape and static guards pass
|
||||
```
|
||||
|
||||
@@ -211,6 +211,14 @@ The per-symbol composite turning a bias into a managed position, **in R**: `stop
|
||||
**Avoid:** —
|
||||
A node that converts a finer stream to a coarser bar stream, emitting a completed bar only at the boundary so no partial bar ever leaks (enforcing no look-ahead). Clock-sensitive.
|
||||
|
||||
### risk regime
|
||||
**Avoid:** stop regime, risk section
|
||||
One entry of a campaign document's structural risk axis (`risk`): a
|
||||
serializable protective-stop regime (sole variant `vol{length,k}`) the matrix
|
||||
runs every cell under, so cells differ by execution discipline, never by
|
||||
signal. Absent or empty = one implicit default regime; the regime's stop
|
||||
defines the risk unit R.
|
||||
|
||||
### run
|
||||
**Avoid:** —
|
||||
One execution recorded in the run registry as a manifest + metrics — the registry-record framing of an execution. Distinct from `sim` (the executable unit) and `backtest` (the replay framing).
|
||||
|
||||
Reference in New Issue
Block a user