From 231188333428acc45c9dc15c1cfdfd26afd7d2f2 Mon Sep 17 00:00:00 2001 From: pkuzic Date: Fri, 25 Sep 2026 09:36:18 +0100 Subject: [PATCH] MONKEY (integration): allow read-only capture CVar fixtures Let deterministic captures read an explicitly selected BENILLA_HOME config while keeping every capture persistence path disabled. This makes the post ON/OFF homes effective without weakening capture hermeticity. Co-Authored-By: GPT-5.6 Sol (Codex) --- crates/benilla-app/src/cvars.rs | 52 ++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/crates/benilla-app/src/cvars.rs b/crates/benilla-app/src/cvars.rs index 36365679..8ceac116 100644 --- a/crates/benilla-app/src/cvars.rs +++ b/crates/benilla-app/src/cvars.rs @@ -2278,7 +2278,7 @@ fn load_config(world: &mut World) { // outlives the GPU it described. cvars.own_for_session("gxApi", None); match stored { - StoredConfig::Absent => {} // no file, hermetic capture, or no install + StoredConfig::Absent => {} // no file, capture without a fixture, or no install StoredConfig::Bad(msg) => { // A malformed file is preserved, not clobbered: nothing loads, but nothing // saves over it either until a change actually happens — and the warn names @@ -2296,7 +2296,7 @@ fn load_config(world: &mut World) { /// What the one read of `config.toml` found. enum StoredConfig { - /// No file, no install, or a hermetic capture — every value is its registered default. + /// No file, no install, or a capture without an explicit fixture — use registered defaults. Absent, /// The file's `[cvars]` table. Table(BTreeMap), @@ -2320,8 +2320,8 @@ enum StoredConfig { /// law can legitimately move under a run. The thing worth having exactly one of is this function, /// not its result. fn stored_config() -> StoredConfig { - let Some(path) = crate::local_state::config_path() else { - return StoredConfig::Absent; // hermetic capture, or no install — session-only state + let Some(path) = config_read_path() else { + return StoredConfig::Absent; // capture without a fixture, or no install — session-only }; let text = match std::fs::read_to_string(&path) { Ok(t) => t, @@ -2337,6 +2337,18 @@ fn stored_config() -> StoredConfig { } } +/// MONKEY (integration): a capture may opt into one explicit, read-only CVar fixture through +/// `BENILLA_HOME`. All other local state stays hermetic and [`save_config`] still sees no path, +/// so deterministic A/B homes can select graphics tiers without a capture writing anything back. +fn config_read_path() -> Option { + if std::env::var_os("WOW_CAPTURE").is_some() { + return std::env::var_os("BENILLA_HOME") + .map(std::path::PathBuf::from) + .map(|home| home.join("config.toml")); + } + crate::local_state::config_path() +} + /// One CVar as `config.toml` holds it, matched case-insensitively — **before the `App` exists** /// (decision 1627). /// @@ -4291,6 +4303,38 @@ mod tests { std::fs::remove_dir_all(&tmp).ok(); } + /// MONKEY (integration): deterministic graphics A/B captures may read one explicit fixture, + /// but the local-state law still exposes no writable capture path. + #[test] + fn a_capture_can_read_an_explicit_cvar_fixture_without_enabling_writes() { + use crate::local_state::test_env::{EnvGuard, ENV_LOCK}; + let _l = ENV_LOCK + .lock() + .unwrap_or_else(std::sync::PoisonError::into_inner); + let tmp = std::env::temp_dir().join(format!( + "benilla-capture-cvar-fixture-{}", + std::process::id() + )); + std::fs::remove_dir_all(&tmp).ok(); + crate::local_state::write_atomic( + &tmp.join("config.toml"), + "[cvars]\nbloom = \"0\"\nsunShafts = \"0\"\ncolorGrading = \"0\"\n", + ) + .unwrap(); + let _h = EnvGuard::set("BENILLA_HOME", tmp.to_str().unwrap()); + let _c = EnvGuard::set("WOW_CAPTURE", "post-lava-searing"); + + assert_eq!(boot_cvar("bloom").as_deref(), Some("0")); + assert_eq!(boot_cvar("sunShafts").as_deref(), Some("0")); + assert_eq!(boot_cvar("colorGrading").as_deref(), Some("0")); + assert_eq!( + crate::local_state::config_path(), + None, + "capture fixtures are read-only; persistence remains hermetic" + ); + std::fs::remove_dir_all(&tmp).ok(); + } + /// **Every registered row has a reader** — the honest-tree rule (module doc, 1140), made /// structural now that the registry has no central arm to answer `false` from. A row's /// reader is a string literal naming it somewhere in the crate's code or `benilla-ui`'s (an