fix(TASK-046): keep inspection leniency off mutating and writing flags

The look-don't-touch bypass was keyed on load_with_interpolation's
pre-existing bool, which mutating early paths (--authenticate, --auth-mcp,
mcp manage, vault flags) already passed as true meaning only 'skip vault
interpolation' — so they silently lost their first-run config creation.
Split the parameter: skip_interpolation keeps the historical meaning,
a distinct inspection flag alone gates the missing-config default/wizard
bypass.

inspection_only is now computed from an explicit read-only whitelist:
--sync-models is excluded (it downloads and writes models-override.yaml)
and returns to the bootstrap/strict path, keeping only its historical
interpolation-skip and vault leniency. mcp/vault inspection sub-conditions
are hoisted and shared so mutating flag combos never inherit the bypass.

Coverage: sandbox vault contract tests, a sync-models bootstrap-path pin,
a --sync-models scenario in usage-probe-inspection.sh, and a PTY wizard
regression probe (scripts/usage-probe-wizard.sh).
This commit is contained in:
Dark-Alex-17 committed 2026-09-16 16:48:57 -06:00
1 parent 9da0b91673
commit d89d24aff4
6 files changed
+269 -32

No files matched your search

+10 -7
View File
@@ -729,14 +729,17 @@ pub fn maybe_spawn_models_refresh(app: &AppConfig) {
}
impl Config {
pub async fn load_with_interpolation(info_flag: bool) -> Result<Self> {
pub async fn load_with_interpolation(
skip_interpolation: bool,
inspection: bool,
) -> Result<Self> {
let config_path = paths::config_file();
if env::var_os(SANDBOX_ENV_FLAG).is_some() {
if !config_path.exists() {
// Inspection-only flags are look-don't-touch: no first-run
// config creation, nothing written.
if info_flag {
if inspection {
return Ok(Self::default());
}
create_config_file(&config_path).await?;
@@ -756,7 +759,7 @@ impl Config {
// Same look-don't-touch rule: never run the interactive
// first-run wizard (or write a config) for a flag that
// only inspects state.
if info_flag {
if inspection {
return Ok(Self::default());
}
if *IS_STDOUT_TERMINAL {
@@ -769,7 +772,7 @@ impl Config {
Self::load_from_file(&config_path)?
};
if info_flag {
if skip_interpolation {
return Ok(config);
}
@@ -1627,7 +1630,7 @@ hooks:
std::env::set_var(crate::sandbox::SANDBOX_ENV_FLAG, "1");
}
let result = Config::load_with_interpolation(false).await;
let result = Config::load_with_interpolation(false, false).await;
let (_, raw) = Config::load_from_file(&config_path).unwrap();
unsafe {
@@ -1666,7 +1669,7 @@ hooks:
let _provider = crate::testing::EnvVarGuard::unset(get_env_name("provider"));
let _platform = crate::testing::EnvVarGuard::unset(get_env_name("platform"));
let result = Config::load_with_interpolation(true).await;
let result = Config::load_with_interpolation(true, true).await;
let leftover: Vec<_> = std::fs::read_dir(&tmp_dir)
.unwrap()
@@ -1694,7 +1697,7 @@ hooks:
crate::testing::EnvVarGuard::set(get_env_name("config_file"), &config_path);
let _sandbox = crate::testing::EnvVarGuard::set(crate::sandbox::SANDBOX_ENV_FLAG, "1");
let result = Config::load_with_interpolation(true).await;
let result = Config::load_with_interpolation(true, true).await;
let created = config_path.exists();
let _ = std::fs::remove_dir_all(&tmp_dir);