Collect unknown config fields and emit a single warning
Instead of one tracing::warn! per field, load_config and load_config_str
now gather all unknown paths into a Vec and emit a single warning with
fields = ?unknown when the vec is non-empty.

https://claude.ai/code/session_015tdp3ahAmmdE69yCsV7J8b
change
commit d991aaf1f0052792ee141da63fa75fe5b8e64a4e
author Claude <noreply@anthropic.com>
date
parent 2b8fe41b
diff --git a/quire-core/src/fennel.rs b/quire-core/src/fennel.rs
index 25e94d7..c5619f8 100644
--- a/quire-core/src/fennel.rs
+++ b/quire-core/src/fennel.rs
@@ -198,29 +198,36 @@ impl Fennel {
     ///
     /// Convenience wrapper for callers that only need a one-shot config load
     /// and don't need to reuse the VM. Warns via `tracing::warn!` for any
-    /// unknown fields.
+    /// unknown fields (all in a single message).
     pub fn load_config<T>(path: &Path) -> Result<T, FennelError>
     where
         T: serde::de::DeserializeOwned,
     {
         let name = path.display().to_string();
-        Self::new()?.load_file(path, |path| {
-            tracing::warn!(config = %name, field = %path, "unknown config field ignored");
-        })
+        let mut unknown = Vec::new();
+        let result = Self::new()?.load_file(path, |path| unknown.push(path.to_string()))?;
+        if !unknown.is_empty() {
+            tracing::warn!(config = %name, fields = ?unknown, "unknown config fields ignored");
+        }
+        Ok(result)
     }
 
     /// Create a fresh Fennel VM and parse `source` into `T`.
     ///
     /// Like [`load_config`] but reads from a string rather than a file.
     /// `name` is used in error messages (e.g. `".quire/config.fnl"`).
-    /// Warns via `tracing::warn!` for any unknown fields.
+    /// Warns via `tracing::warn!` for any unknown fields (all in a single message).
     pub fn load_config_str<T>(source: &str, name: &str) -> Result<T, FennelError>
     where
         T: serde::de::DeserializeOwned,
     {
-        Self::new()?.load_string(source, name, |path| {
-            tracing::warn!(config = %name, field = %path, "unknown config field ignored");
-        })
+        let mut unknown = Vec::new();
+        let result =
+            Self::new()?.load_string(source, name, |path| unknown.push(path.to_string()))?;
+        if !unknown.is_empty() {
+            tracing::warn!(config = %name, fields = ?unknown, "unknown config fields ignored");
+        }
+        Ok(result)
     }
 }