audit: cycle 0110 tidy — verb-dissolution cycle 1 closed drift-clean
Architect review (scope e7c7bde..b7aaa0b) found three items; resolutions: - fix: docs/authoring-guide.md + glossary aligned with the shipped vocabulary (std::sweep selection group optional/all-or-nothing, selection-free = terminal-only, worked example verified against the live binary). - fix: the #203 wrapped/raw axis-name convention is now single-sourced in campaign_run.rs (wrapped_to_raw_axis + raw_matches_wrapped, a cross-documented inverse pair; dispatch_sweep and both bind sites call through it; inverse-property unit test). Ratify note: this unifies two previously different algorithms onto the documented first-segment semantics — the old loose ends_with suffix-match also accepted sub-segment shorthands (e.g. axis "length" against "sma_signal.fast.length") that no stored document or test used; such shorthands now refuse loudly (unbound/unknown axis) instead of matching, the refuse-don't-guess reading of #203. - carry: run_blueprint_sweep/blueprint_sweep_family remain generically real-capable though the dispatch no longer routes real data to them; the residual capability is shared DataSource machinery, its removal falls out with the built-in/synthetic branch retirement (#159), and the synthetic-only status is documented at the fn. What holds: C24 canonical form (flatten group leaves every stored content id unchanged, golden pin untouched), C18 lineage (topology_hash IS content_id_of, single store write resolves the strategy ref; selection-free arm still persists the full family), C25 closed- vocabulary discipline (all-or-nothing group, Blockly-clean), C3 (ms/ns conversion through the ingest seam's own named fn). Ledger: #109 open-thread status lifted to cycles 0107-0110 reality (executor shipped, amendment package landed, verb dissolution running as milestone #210 with sweep dissolved). Regression gates: cargo test --workspace 1056/0; clippy -D warnings clean; cargo doc clean. Cycle spec+plan removed at close per convention. refs #210
This commit is contained in:
@@ -123,11 +123,34 @@ struct CampaignRunLine<'a> {
|
||||
campaign_run: &'a CampaignRunRecord,
|
||||
}
|
||||
|
||||
/// Suffix-join each raw campaign axis onto exactly one wrapped param (wrapped
|
||||
/// == raw, or wrapped ends with ".{raw}" — the established suffix-match
|
||||
/// pattern), then require every wrapped slot to be covered. Pure and
|
||||
/// independent of the env/data seam so the three fault arms are unit-testable
|
||||
/// without a project, a store, or a loaded blueprint.
|
||||
/// Strip the wrapper's exactly-one leading node segment from a WRAPPED param
|
||||
/// path, yielding the RAW campaign-axis name the doc speaks (#203): the bind
|
||||
/// convention prefixes a strategy's own param paths with one root-composite
|
||||
/// segment before they enter the runnable (wrapped) param space, so the raw
|
||||
/// axis is everything after that segment's dot. Returns `name` unchanged if
|
||||
/// there is no dot (defensive; every wrapped param space produced by
|
||||
/// `blueprint_axis_probe` has one). This is the single definition of "the
|
||||
/// wrapper segment" — [`raw_matches_wrapped`] is built on it so a wrap-depth
|
||||
/// change cannot desync the two directions of the #203 convention.
|
||||
pub(crate) fn wrapped_to_raw_axis(name: &str) -> &str {
|
||||
name.split_once('.').map(|(_, rest)| rest).unwrap_or(name)
|
||||
}
|
||||
|
||||
/// Does a RAW campaign-axis name (the doc's own namespace) address this ONE
|
||||
/// wrapped param path? True for an exact match (an unwrapped param, if one
|
||||
/// ever exists) or when stripping the wrapper's one node segment
|
||||
/// ([`wrapped_to_raw_axis`]) yields exactly `raw`. Inverse of
|
||||
/// `wrapped_to_raw_axis`, co-located and built on it (#203): the two are the
|
||||
/// only two places the wrapper-segment convention is expressed.
|
||||
pub(crate) fn raw_matches_wrapped(raw: &str, wrapped: &str) -> bool {
|
||||
wrapped == raw || wrapped_to_raw_axis(wrapped) == raw
|
||||
}
|
||||
|
||||
/// Suffix-join each raw campaign axis onto exactly one wrapped param
|
||||
/// ([`raw_matches_wrapped`] — wrapped == raw, or stripping the wrapper's one
|
||||
/// node segment yields raw), then require every wrapped slot to be covered.
|
||||
/// Pure and independent of the env/data seam so the three fault arms are
|
||||
/// unit-testable without a project, a store, or a loaded blueprint.
|
||||
fn bind_axes(
|
||||
space: &[ParamSpec],
|
||||
strategy_id: &str,
|
||||
@@ -138,7 +161,7 @@ fn bind_axes(
|
||||
let hits: Vec<usize> = space
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, p)| p.name == *raw || p.name.ends_with(&format!(".{raw}")))
|
||||
.filter(|(_, p)| raw_matches_wrapped(raw, &p.name))
|
||||
.map(|(i, _)| i)
|
||||
.collect();
|
||||
match hits.as_slice() {
|
||||
@@ -166,8 +189,7 @@ fn bind_axes(
|
||||
// Speak the RAW campaign-axis namespace (the doc's own): the
|
||||
// wrapped space prefixes the signal's params with one node
|
||||
// segment, so the raw path is everything after it (#203).
|
||||
let raw =
|
||||
spec.name.split_once('.').map(|(_, rest)| rest).unwrap_or(&spec.name);
|
||||
let raw = wrapped_to_raw_axis(&spec.name);
|
||||
return Err(MemberFault::Bind(format!(
|
||||
"open param \"{raw}\" of strategy {strategy_id} is bound by no \
|
||||
campaign axis (wrapped path: {})",
|
||||
@@ -803,6 +825,18 @@ mod tests {
|
||||
assert!(!is_content_id(&format!("{}g", "a".repeat(63))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// #203: `wrapped_to_raw_axis` and `raw_matches_wrapped` are the inverse
|
||||
/// pair of one wrapper-segment convention — stripping the wrapper off a
|
||||
/// wrapped name must satisfy the match predicate against that SAME
|
||||
/// wrapped name, and a raw name from a DIFFERENT wrapped param must not.
|
||||
fn raw_matches_wrapped_and_wrapped_to_raw_axis_are_inverses() {
|
||||
let wrapped = "sma_signal.fast.length";
|
||||
assert_eq!(wrapped_to_raw_axis(wrapped), "fast.length");
|
||||
assert!(raw_matches_wrapped(wrapped_to_raw_axis(wrapped), wrapped));
|
||||
assert!(!raw_matches_wrapped("fast.length", "sma_signal.slow.length"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// bind_axes resolves a raw axis name against the ONE wrapped param whose
|
||||
/// path ends with ".{raw}" (or equals it), producing a co-indexed point.
|
||||
|
||||
@@ -4529,11 +4529,7 @@ fn dispatch_sweep(a: SweepCmd, env: &project::Env) {
|
||||
// `param_space`) refuses every axis as unknown.
|
||||
let raw_axes: Vec<(String, Vec<Scalar>)> = axes
|
||||
.iter()
|
||||
.map(|(n, v)| {
|
||||
let raw =
|
||||
n.split_once('.').map(|(_, rest)| rest.to_string()).unwrap_or_else(|| n.clone());
|
||||
(raw, v.clone())
|
||||
})
|
||||
.map(|(n, v)| (campaign_run::wrapped_to_raw_axis(n).to_string(), v.clone()))
|
||||
.collect();
|
||||
verb_sugar::run_sweep_sugar(
|
||||
&raw_axes, &name, &symbol, from_ms, to_ms, &canonical, env,
|
||||
|
||||
Reference in New Issue
Block a user