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:
2026-07-09 18:19:01 +02:00
parent a74c3d116e
commit 6e22c98e23
3 changed files with 87 additions and 4 deletions
+65 -1
View File
@@ -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");