fix(cli): doctor config check hides non-existent candidate paths

Before: doctor reported 'loaded 0/5' and listed 5 'Discovered file'
entries for paths that don't exist on disk. This looked like 5 files
failed to load, when in fact they are just standard search locations.

After: only paths that actually exist on disk are shown as 'Discovered
file'. 'loaded N/M' denominator is now the count of present files, not
candidate paths. With no config files present: 'loaded 0/0' +
'Discovered files  <none> (defaults active)'.

159 CLI tests pass.
This commit is contained in:
YeonGyu-Kim
2026-04-10 02:32:47 +09:00
parent 57943b17f3
commit c8cac7cae8

View File

@@ -1657,6 +1657,16 @@ fn check_config_health(
) -> DiagnosticCheck { ) -> DiagnosticCheck {
let discovered = config_loader.discover(); let discovered = config_loader.discover();
let discovered_count = discovered.len(); let discovered_count = discovered.len();
// Separate candidate paths that actually exist from those that don't.
// Showing non-existent paths as "Discovered file" implies they loaded
// but something went wrong, which is confusing. We only surface paths
// that exist on disk as discovered; non-existent ones are silently
// omitted from the display (they are just the standard search locations).
let present_paths: Vec<String> = discovered
.iter()
.filter(|e| e.path.exists())
.map(|e| e.path.display().to_string())
.collect();
let discovered_paths = discovered let discovered_paths = discovered
.iter() .iter()
.map(|entry| entry.path.display().to_string()) .map(|entry| entry.path.display().to_string())
@@ -1664,10 +1674,11 @@ fn check_config_health(
match config { match config {
Ok(runtime_config) => { Ok(runtime_config) => {
let loaded_entries = runtime_config.loaded_entries(); let loaded_entries = runtime_config.loaded_entries();
let loaded_count = loaded_entries.len();
let present_count = present_paths.len();
let mut details = vec![format!( let mut details = vec![format!(
"Config files loaded {}/{}", "Config files loaded {}/{}",
loaded_entries.len(), loaded_count, present_count
discovered_count
)]; )];
if let Some(model) = runtime_config.model() { if let Some(model) = runtime_config.model() {
details.push(format!("Resolved model {model}")); details.push(format!("Resolved model {model}"));
@@ -1676,39 +1687,29 @@ fn check_config_health(
"MCP servers {}", "MCP servers {}",
runtime_config.mcp().servers().len() runtime_config.mcp().servers().len()
)); ));
if discovered_paths.is_empty() { if present_paths.is_empty() {
details.push("Discovered files <none>".to_string()); details.push("Discovered files <none> (defaults active)".to_string());
} else { } else {
details.extend( details.extend(
discovered_paths present_paths
.iter() .iter()
.map(|path| format!("Discovered file {path}")), .map(|path| format!("Discovered file {path}")),
); );
} }
DiagnosticCheck::new( DiagnosticCheck::new(
"Config", "Config",
if discovered_count == 0 { DiagnosticLevel::Ok,
DiagnosticLevel::Warn if present_count == 0 {
} else { "no config files present; defaults are active"
DiagnosticLevel::Ok
},
if discovered_count == 0 {
"no config files were found; defaults are active"
} else { } else {
"runtime config loaded successfully" "runtime config loaded successfully"
}, },
) )
.with_details(details) .with_details(details)
.with_data(Map::from_iter([ .with_data(Map::from_iter([
("discovered_files".to_string(), json!(discovered_paths)), ("discovered_files".to_string(), json!(present_paths)),
( ("discovered_files_count".to_string(), json!(present_count)),
"discovered_files_count".to_string(), ("loaded_config_files".to_string(), json!(loaded_count)),
json!(discovered_count),
),
(
"loaded_config_files".to_string(),
json!(loaded_entries.len()),
),
("resolved_model".to_string(), json!(runtime_config.model())), ("resolved_model".to_string(), json!(runtime_config.model())),
( (
"mcp_servers".to_string(), "mcp_servers".to_string(),