1use crate::deployment::DeploymentGrade;
19
20#[derive(Debug, Default, Clone, PartialEq, Eq)]
24pub struct InternalFeatureFlags {
25 pub docker_capability: bool,
28 pub container_sandbox: bool,
34 pub session_sandbox: bool,
37 pub lua: bool,
41}
42
43impl InternalFeatureFlags {
44 pub fn from_env() -> Self {
46 let docker_capability = standard_flag("FEATURE_DOCKER_CAPABILITY", false);
47
48 Self {
49 docker_capability,
50 container_sandbox: standard_flag("FEATURE_CONTAINER_SANDBOX", docker_capability),
51 session_sandbox: standard_flag("FEATURE_SESSION_SANDBOX", false),
52 lua: standard_flag("FEATURE_LUA", false),
53 }
54 }
55
56 pub fn is_enabled(&self, flag: &str) -> bool {
58 match flag {
59 "docker_capability" => self.docker_capability,
60 "container_sandbox" => self.container_sandbox,
61 "session_sandbox" => self.session_sandbox,
62 "lua" => self.lua,
63 _ => false,
64 }
65 }
66}
67
68#[derive(Debug, Clone)]
78pub struct ExecutionFeatureDecisions {
79 pub agent_delegation: bool,
83 pub internal: InternalFeatureFlags,
85}
86
87impl ExecutionFeatureDecisions {
88 pub fn from_env(grade: DeploymentGrade) -> Self {
90 Self {
91 agent_delegation: experimental_flag("FEATURE_AGENT_DELEGATION", &grade),
92 internal: InternalFeatureFlags::from_env(),
93 }
94 }
95
96 pub fn is_enabled(&self, flag: &str) -> bool {
107 match flag {
108 "docker_capability" | "container_sandbox" | "session_sandbox" | "lua" => {
109 self.internal.is_enabled(flag)
110 }
111 "agent_delegation" => self.agent_delegation,
112 _ => {
113 let env_var = format!("FEATURE_{}", flag.to_ascii_uppercase());
114 standard_flag(&env_var, false)
115 }
116 }
117 }
118}
119
120pub fn experimental_flag(env_var: &str, grade: &DeploymentGrade) -> bool {
124 if let Ok(val) = std::env::var(env_var) {
125 return val == "true" || val == "1";
126 }
127 grade.experimental_features_enabled()
128}
129
130pub fn standard_flag(env_var: &str, default: bool) -> bool {
134 std::env::var(env_var)
135 .map(|v| v == "true" || v == "1")
136 .unwrap_or(default)
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn internal_lookup_reads_each_flag_independently() {
145 for values in [
146 [false, false, false, false],
147 [true, false, false, false],
148 [false, true, false, false],
149 [false, false, true, false],
150 [false, false, false, true],
151 ] {
152 let flags = InternalFeatureFlags {
153 docker_capability: values[0],
154 container_sandbox: values[1],
155 session_sandbox: values[2],
156 lua: values[3],
157 };
158 for (name, expected) in [
159 "docker_capability",
160 "container_sandbox",
161 "session_sandbox",
162 "lua",
163 ]
164 .into_iter()
165 .zip(values)
166 {
167 assert_eq!(flags.is_enabled(name), expected, "{name}, {values:?}");
168 }
169 for unknown in ["nonexistent", "Docker_capability", ""] {
170 assert!(!flags.is_enabled(unknown));
171 }
172 }
173 }
174
175 #[test]
176 fn environment_overrides_and_grade_defaults_are_isolated() {
177 const CHILD: &str = "EVERRUNS_EXECUTION_FEATURE_REVIEW_CASE";
178 const KEYS: &[&str] = &[
179 "FEATURE_DOCKER_CAPABILITY",
180 "FEATURE_CONTAINER_SANDBOX",
181 "FEATURE_SESSION_SANDBOX",
182 "FEATURE_LUA",
183 "FEATURE_AGENT_DELEGATION",
184 "FEATURE_MACHINE_PAYMENTS",
185 "DEPLOYMENT_GRADE",
186 "DEV_MODE",
187 "FEATURE_REVIEW_MISSING",
188 ];
189 struct Case {
190 name: &'static str,
191 env: &'static [(&'static str, &'static str)],
192 internal: [bool; 4],
193 delegation: [bool; 4],
194 grade: DeploymentGrade,
195 payments: bool,
196 }
197 let cases = [
198 Case {
199 name: "unset",
200 env: &[],
201 internal: [false, false, false, false],
202 delegation: [true, false, false, false],
203 grade: DeploymentGrade::Prod,
204 payments: false,
205 },
206 Case {
207 name: "legacy docker and dev mode one",
208 env: &[("FEATURE_DOCKER_CAPABILITY", "true"), ("DEV_MODE", "1")],
209 internal: [true, true, false, false],
210 delegation: [true, false, false, false],
211 grade: DeploymentGrade::Dev,
212 payments: false,
213 },
214 Case {
215 name: "explicit container and preview grade",
216 env: &[
217 ("FEATURE_CONTAINER_SANDBOX", "1"),
218 ("DEPLOYMENT_GRADE", "staging"),
219 ("DEV_MODE", "true"),
220 ],
221 internal: [false, true, false, false],
222 delegation: [true, false, false, false],
223 grade: DeploymentGrade::Preview,
224 payments: false,
225 },
226 Case {
227 name: "explicit container false overrides legacy",
228 env: &[
229 ("FEATURE_DOCKER_CAPABILITY", "true"),
230 ("FEATURE_CONTAINER_SANDBOX", "false"),
231 ("DEPLOYMENT_GRADE", "PoC"),
232 ],
233 internal: [true, false, false, false],
234 delegation: [true, false, false, false],
235 grade: DeploymentGrade::Poc,
236 payments: false,
237 },
238 Case {
239 name: "all enabled with explicit production",
240 env: &[
241 ("FEATURE_DOCKER_CAPABILITY", "1"),
242 ("FEATURE_CONTAINER_SANDBOX", "true"),
243 ("FEATURE_SESSION_SANDBOX", "true"),
244 ("FEATURE_LUA", "1"),
245 ("FEATURE_AGENT_DELEGATION", "true"),
246 ("FEATURE_MACHINE_PAYMENTS", "1"),
247 ("DEPLOYMENT_GRADE", "production"),
248 ("DEV_MODE", "true"),
249 ],
250 internal: [true, true, true, true],
251 delegation: [true, true, true, true],
252 grade: DeploymentGrade::Prod,
253 payments: true,
254 },
255 Case {
256 name: "explicit false overrides dev defaults",
257 env: &[
258 ("FEATURE_DOCKER_CAPABILITY", "false"),
259 ("FEATURE_CONTAINER_SANDBOX", "0"),
260 ("FEATURE_SESSION_SANDBOX", "false"),
261 ("FEATURE_LUA", "0"),
262 ("FEATURE_AGENT_DELEGATION", "false"),
263 ("FEATURE_MACHINE_PAYMENTS", "false"),
264 ("DEV_MODE", "true"),
265 ],
266 internal: [false, false, false, false],
267 delegation: [false, false, false, false],
268 grade: DeploymentGrade::Dev,
269 payments: false,
270 },
271 Case {
272 name: "invalid explicit values fail closed",
273 env: &[
274 ("FEATURE_DOCKER_CAPABILITY", "true"),
275 ("FEATURE_CONTAINER_SANDBOX", "TRUE"),
276 ("FEATURE_SESSION_SANDBOX", "typo"),
277 ("FEATURE_LUA", "TRUE"),
278 ("FEATURE_AGENT_DELEGATION", "TRUE"),
279 ("FEATURE_MACHINE_PAYMENTS", "yes"),
280 ("DEPLOYMENT_GRADE", "invalid"),
281 ("DEV_MODE", "true"),
282 ],
283 internal: [true, false, false, false],
284 delegation: [false, false, false, false],
285 grade: DeploymentGrade::Prod,
286 payments: false,
287 },
288 Case {
289 name: "empty explicit grade suppresses legacy dev",
290 env: &[("DEPLOYMENT_GRADE", ""), ("DEV_MODE", "true")],
291 internal: [false, false, false, false],
292 delegation: [true, false, false, false],
293 grade: DeploymentGrade::Prod,
294 payments: false,
295 },
296 ];
297 if let Ok(index) = std::env::var(CHILD) {
298 let index: usize = index.parse().unwrap();
299 let case = &cases[index];
300 let expected_internal = InternalFeatureFlags {
301 docker_capability: case.internal[0],
302 container_sandbox: case.internal[1],
303 session_sandbox: case.internal[2],
304 lua: case.internal[3],
305 };
306 assert_eq!(
307 InternalFeatureFlags::from_env(),
308 expected_internal,
309 "{}",
310 case.name
311 );
312 assert_eq!(DeploymentGrade::from_env(), case.grade, "{}", case.name);
313 for (grade, expected_delegation) in [
314 DeploymentGrade::Dev,
315 DeploymentGrade::Poc,
316 DeploymentGrade::Preview,
317 DeploymentGrade::Prod,
318 ]
319 .into_iter()
320 .zip(case.delegation)
321 {
322 let decisions = ExecutionFeatureDecisions::from_env(grade);
323 assert_eq!(
324 decisions.internal, expected_internal,
325 "{}, {grade}",
326 case.name
327 );
328 assert_eq!(
329 decisions.agent_delegation, expected_delegation,
330 "{}, {grade}",
331 case.name
332 );
333 assert_eq!(
334 decisions.is_enabled("agent_delegation"),
335 expected_delegation
336 );
337 for (name, expected) in [
338 "docker_capability",
339 "container_sandbox",
340 "session_sandbox",
341 "lua",
342 ]
343 .into_iter()
344 .zip(case.internal)
345 {
346 assert_eq!(decisions.is_enabled(name), expected, "{name}");
347 }
348 assert_eq!(decisions.is_enabled("machine_payments"), case.payments);
349 assert_eq!(decisions.is_enabled("MACHINE_PAYMENTS"), case.payments);
350 assert!(!decisions.is_enabled("review_missing"));
351 }
352 let captured = ExecutionFeatureDecisions {
355 agent_delegation: false,
356 internal: InternalFeatureFlags::default(),
357 };
358 for name in [
359 "docker_capability",
360 "container_sandbox",
361 "session_sandbox",
362 "lua",
363 "agent_delegation",
364 ] {
365 assert!(!captured.is_enabled(name), "captured {name}");
366 }
367 assert_eq!(captured.is_enabled("machine_payments"), case.payments);
368 assert!(!standard_flag("FEATURE_REVIEW_MISSING", false));
369 assert!(standard_flag("FEATURE_REVIEW_MISSING", true));
370 println!("feature fixture {index} completed");
371 return;
372 }
373 for (index, case) in cases.iter().enumerate() {
374 let mut command = std::process::Command::new(std::env::current_exe().unwrap());
377 command.args([
378 "--exact",
379 concat!(
380 module_path!(),
381 "::environment_overrides_and_grade_defaults_are_isolated"
382 )
383 .strip_prefix("everruns_core::")
384 .unwrap(),
385 "--nocapture",
386 ]);
387 for key in KEYS {
388 command.env_remove(key);
389 }
390 let output = command
391 .envs(case.env.iter().copied())
392 .env(CHILD, index.to_string())
393 .output()
394 .unwrap();
395 let stdout = String::from_utf8_lossy(&output.stdout);
396 assert!(
397 output.status.success(),
398 "{} failed:\n{stdout}\n{}",
399 case.name,
400 String::from_utf8_lossy(&output.stderr)
401 );
402 assert!(
403 stdout.contains(&format!("feature fixture {index} completed")),
404 "{} did not run assertions:\n{stdout}",
405 case.name
406 );
407 }
408 }
409}