From d3c8ff6abee69a41523e3dfe5f8b5fe55bf75305 Mon Sep 17 00:00:00 2001 From: Argenis Date: Sun, 22 Feb 2026 12:16:54 -0500 Subject: [PATCH] feat(config): warn on unknown config keys to prevent silent misconfig (#1410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci(homebrew): prefer HOMEBREW_UPSTREAM_PR_TOKEN with fallback * ci(homebrew): handle existing upstream remote and main base * fix(skills): allow cross-skill references in open-skills audit Issue: #1391 The skill audit was too strict when validating markdown links in open-skills, causing many skills to fail loading with errors like: - "absolute markdown link paths are not allowed (../other-skill/SKILL.md)" - "markdown link points to a missing file (skill-name.md)" Root cause: 1. `looks_like_absolute_path()` rejected paths starting with ".." before canonicalization could validate they stay within root 2. Missing file errors were raised for cross-skill references that are valid but point to skills not installed locally Fix: 1. Allow ".." paths to pass through to canonicalization check which properly validates they resolve within the skill root 2. Treat cross-skill references (parent dir traversal or bare .md filenames) as non-fatal when pointing to missing files Cross-skill references are identified by: - Parent directory traversal: `../other-skill/SKILL.md` - Bare skill filename: `other-skill.md` - Explicit relative: `./other-skill.md` Added 6 new tests to cover edge cases for cross-skill references. Co-Authored-By: Claude Opus 4.6 * feat(config): warn on unknown config keys to prevent silent misconfig Issue: #1304 When users configure `[providers.ollama]` with `api_url`, the setting is silently ignored because `[providers.*]` sections don't exist in the config schema. This causes Ollama to always use localhost:11434 regardless of the configured URL. Fix: Use serde_ignored to detect and warn about unknown config keys at load time. This helps users identify misconfigurations like: - `[providers.ollama]` (should be top-level `api_url`) - Typos in section names - Deprecated/removed options The warning is non-blocking - config still loads, but users see: ``` WARN Unknown config key ignored: "providers". Check config.toml... ``` This follows the fail-fast/explicit errors principle (CLAUDE.md ยง3.5). Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Will Sarg <12886992+willsarg@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + src/config/schema.rs | 21 +++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18a57f097..137750b0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5681,6 +5681,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_ignored" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115dffd5f3853e06e746965a20dcbae6ee747ae30b543d91b0e089668bb07798" +dependencies = [ + "serde", + "serde_core", +] + [[package]] name = "serde_json" version = "1.0.149" @@ -8053,6 +8063,7 @@ dependencies = [ "schemars", "serde", "serde-big-array", + "serde_ignored", "serde_json", "sha2", "shellexpand", diff --git a/Cargo.toml b/Cargo.toml index 0d27f198a..edcc12356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ matrix-sdk = { version = "0.16", optional = true, default-features = false, feat # Serialization serde = { version = "1.0", default-features = false, features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = ["std"] } +serde_ignored = "0.1" # Config directories = "6.0" diff --git a/src/config/schema.rs b/src/config/schema.rs index e758a72f7..20f05947b 100644 --- a/src/config/schema.rs +++ b/src/config/schema.rs @@ -3880,8 +3880,25 @@ impl Config { let contents = fs::read_to_string(&config_path) .await .context("Failed to read config file")?; - let mut config: Config = - toml::from_str(&contents).context("Failed to parse config file")?; + + // Track ignored/unknown config keys to warn users about silent misconfigurations + // (e.g., using [providers.ollama] which doesn't exist instead of top-level api_url) + let mut ignored_paths: Vec = Vec::new(); + let mut config: Config = serde_ignored::deserialize( + toml::de::Deserializer::parse(&contents).context("Failed to parse config file")?, + |path| { + ignored_paths.push(path.to_string()); + }, + ) + .context("Failed to deserialize config file")?; + + // Warn about each unknown config key + for path in ignored_paths { + tracing::warn!( + "Unknown config key ignored: \"{}\". Check config.toml for typos or deprecated options.", + path + ); + } // Set computed paths that are skipped during serialization config.config_path = config_path.clone(); config.workspace_dir = workspace_dir;