1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
// Copyright 2024-2026 Jonathan Shook
// SPDX-License-Identifier: Apache-2.0
//! Phase-scoped CLI parameter overrides — SRD 71 P3.
//!
//! A CLI argument whose key contains a `.` is a phase-scoped
//! override: `<phase-pattern>.<param>=<value>`. The pattern part
//! reuses the [`crate::phase_filter::PhasePattern`] dialects
//! (bareword / glob / regex), so `mytestphase42.cursor=fib:7`
//! pins one phase and `*_query.cursor=0..10%` sweeps every
//! query phase.
//!
//! Resolution at each phase activation (highest wins):
//!
//! 1. A **literal** (exact-name) override.
//! 2. A single matching glob / regex override. Two *distinct*
//! non-literal patterns matching the same phase for the same
//! param is a fatal ambiguity — the operator must
//! disambiguate.
//! 3. The workload-wide CLI param / workload `params:` default
//! (untouched by this module — overrides shadow the param on
//! the phase's kernel locally, so everything below the phase
//! resolves the overridden value through the standard scope
//! chain).
//!
//! Every override pattern must match at least one phase name at
//! startup — a pattern that can never fire is a typo, not a
//! preference.
use crate::phase_filter::{PhaseDialect, PhasePattern};
/// One parsed `<phase-pattern>.<param>=<value>` CLI override.
#[derive(Debug, Clone)]
pub struct PhaseParamOverride {
/// Compiled phase-name matcher (literal / glob / regex).
pub pattern: PhasePattern,
/// The workload param the override shadows.
pub param: String,
/// The override value (quote-elided, verbatim otherwise).
pub value: String,
}
/// Scan raw CLI args for phase-scoped overrides. Keys containing
/// a `.` (after quote elision and leading-dash stripping) split
/// at the **last** dot into `(phase-pattern, param)` — phase
/// names never contain dots, and param names are single
/// identifiers, so the last dot is unambiguous even for regex
/// patterns that themselves contain dots (`pha.e42.cursor=…`).
///
/// Returns an error for a key with an empty pattern or param
/// part, or a pattern that fails to compile.
pub fn parse_overrides(args: &[String]) -> Result<Vec<PhaseParamOverride>, String> {
let mut out = Vec::new();
for arg in args {
let unquoted = crate::runner::elide_outer_quotes(arg.as_str());
let stripped = unquoted.trim_start_matches('-');
let Some(eq_pos) = stripped.find('=') else {
continue;
};
let key = &stripped[..eq_pos];
if !key.contains('.') {
continue;
}
// Path-shaped keys aren't overrides (e.g. an operator
// habit like `./foo=bar` or a windows path) — phase
// patterns never contain a slash.
if key.contains('/') || key.contains('\\') {
continue;
}
let value = crate::runner::elide_outer_quotes(&stripped[eq_pos + 1..]).to_string();
let Some((pattern_src, param)) = key.rsplit_once('.') else {
continue;
};
if pattern_src.is_empty() || param.is_empty() {
return Err(format!(
"phase-scoped override `{key}=` needs both a phase pattern and a \
param name: `<phase-pattern>.<param>=<value>`"
));
}
let pattern = PhasePattern::parse(pattern_src)
.map_err(|e| format!("phase-scoped override `{key}=`: {e}"))?;
out.push(PhaseParamOverride {
pattern,
param: param.to_string(),
value,
});
}
Ok(out)
}
/// Startup validation: every override pattern must match at
/// least one declared phase name. A pattern that can never fire
/// is a typo, and silently carrying it would be exactly the
/// "why didn't my override apply" trap this surface exists to
/// avoid.
pub fn validate_against_phases<'a>(
overrides: &[PhaseParamOverride],
phase_names: impl Iterator<Item = &'a str> + Clone,
) -> Result<(), String> {
for ov in overrides {
if !phase_names.clone().any(|n| ov.pattern.is_match(n)) {
return Err(format!(
"phase-scoped override `{}.{}=…` matches no phase \
(dialect: {}). Check the pattern against the workload's \
phase names.",
ov.pattern.source(),
ov.param,
ov.pattern.dialect().as_str(),
));
}
}
Ok(())
}
/// Resolve the overrides that apply to one phase. Per param:
/// a literal (exact-name) match beats any glob/regex match; two
/// distinct non-literal patterns matching for the same param is
/// a fatal ambiguity.
pub fn resolve_for_phase<'a>(
overrides: &'a [PhaseParamOverride],
phase_name: &str,
) -> Result<Vec<(&'a PhaseParamOverride, PhaseDialect)>, String> {
use std::collections::HashMap;
let mut chosen: HashMap<&str, (&PhaseParamOverride, PhaseDialect)> = HashMap::new();
for ov in overrides {
if !ov.pattern.is_match(phase_name) {
continue;
}
let dialect = ov.pattern.dialect();
match chosen.get(ov.param.as_str()) {
None => {
chosen.insert(ov.param.as_str(), (ov, dialect));
}
Some((prev, _prev_dialect)) => {
// A negated pattern (`!foo`) spans many phases, so it
// never wins exact-beats-glob precedence even when its
// body is a literal — `is_exact_literal` folds that in.
let prev_is_literal = prev.pattern.is_exact_literal();
let this_is_literal = ov.pattern.is_exact_literal();
match (prev_is_literal, this_is_literal) {
// Exact beats pattern.
(true, false) => {}
(false, true) => {
chosen.insert(ov.param.as_str(), (ov, dialect));
}
// Two distinct patterns (or two literals,
// which means a repeated key with different
// values survived parsing) for the same
// param: ambiguous — the operator must
// disambiguate.
_ => {
if prev.pattern.source() != ov.pattern.source() || prev.value != ov.value {
return Err(format!(
"ambiguous phase-scoped overrides for param `{}` on \
phase `{phase_name}`: both `{}.{}=` and `{}.{}=` \
match. Disambiguate the patterns (an exact phase \
name beats a glob).",
ov.param,
prev.pattern.source(),
prev.param,
ov.pattern.source(),
ov.param,
));
}
}
}
}
}
}
Ok(chosen.into_values().collect())
}
#[cfg(test)]
mod tests {
use super::*;
fn args(items: &[&str]) -> Vec<String> {
items.iter().map(|s| s.to_string()).collect()
}
#[test]
fn parses_literal_glob_and_skips_plain_params() {
let ovs = parse_overrides(&args(&[
"workload=foo.yaml",
"cursor=0..50%",
"phase42.cursor=fib:7",
"*_query.cursor=0..10%",
]))
.unwrap();
assert_eq!(ovs.len(), 2);
assert_eq!(ovs[0].pattern.source(), "phase42");
assert_eq!(ovs[0].param, "cursor");
assert_eq!(ovs[0].value, "fib:7");
assert_eq!(ovs[1].pattern.dialect(), PhaseDialect::Glob);
}
#[test]
fn path_shaped_keys_are_not_overrides() {
let ovs = parse_overrides(&args(&["./local.path=x", "a/b.c=y"])).unwrap();
assert!(ovs.is_empty());
}
#[test]
fn quote_elision_applies_to_overrides() {
let ovs = parse_overrides(&args(&["'*_query.cursor=90%,*/10'"])).unwrap();
assert_eq!(ovs.len(), 1);
assert_eq!(ovs[0].value, "90%,*/10");
}
#[test]
fn validate_rejects_never_matching_pattern() {
let ovs = parse_overrides(&args(&["nosuch_*.cursor=fib:7"])).unwrap();
let err = validate_against_phases(&ovs, ["alpha", "beta"].into_iter()).unwrap_err();
assert!(err.contains("matches no phase"), "diagnostic: {err}");
}
#[test]
fn exact_beats_glob_for_same_param() {
let ovs =
parse_overrides(&args(&["*_query.cursor=0..10%", "ann_query.cursor=fib:7"])).unwrap();
let chosen = resolve_for_phase(&ovs, "ann_query").unwrap();
assert_eq!(chosen.len(), 1);
assert_eq!(chosen[0].0.value, "fib:7");
// A phase only the glob matches gets the glob value.
let chosen = resolve_for_phase(&ovs, "pvs_query").unwrap();
assert_eq!(chosen.len(), 1);
assert_eq!(chosen[0].0.value, "0..10%");
}
#[test]
fn two_globs_matching_same_phase_is_ambiguous() {
let ovs =
parse_overrides(&args(&["ann_*.cursor=0..10%", "*_query.cursor=0..20%"])).unwrap();
let err = resolve_for_phase(&ovs, "ann_query").unwrap_err();
assert!(err.contains("ambiguous"), "diagnostic: {err}");
}
#[test]
fn distinct_params_resolve_independently() {
let ovs = parse_overrides(&args(&["ann_query.cursor=fib:7", "ann_query.k=100"])).unwrap();
let mut chosen = resolve_for_phase(&ovs, "ann_query").unwrap();
chosen.sort_by_key(|(ov, _)| ov.param.clone());
assert_eq!(chosen.len(), 2);
assert_eq!(chosen[0].0.param, "cursor");
assert_eq!(chosen[1].0.param, "k");
}
}