diff --git a/codex-rs/config/src/profile_toml.rs b/codex-rs/config/src/profile_toml.rs index 5beb838d54..53f32f3fba 100644 --- a/codex-rs/config/src/profile_toml.rs +++ b/codex-rs/config/src/profile_toml.rs @@ -75,7 +75,6 @@ pub struct ConfigProfile { /// TUI settings supported inside a named profile. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct ProfileTui { /// Preferred layout for resume/fork session picker results. diff --git a/codex-rs/config/src/tui_keymap.rs b/codex-rs/config/src/tui_keymap.rs index c59b79e292..170dcf1b44 100644 --- a/codex-rs/config/src/tui_keymap.rs +++ b/codex-rs/config/src/tui_keymap.rs @@ -6,8 +6,8 @@ //! //! Responsibilities: //! -//! 1. Define strongly typed config contexts/actions with unknown-field -//! rejection. +//! 1. Define strongly typed config contexts/actions. Unknown fields are ignored +//! during deserialization and reported by the shared strict-config policy. //! 2. Normalize accepted key aliases into canonical names. //! 3. Reject malformed bindings early with user-facing diagnostics. //! @@ -131,7 +131,6 @@ impl KeybindingsSpec { /// Global keybindings. These are used when a context does not define an override. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiGlobalKeymap { /// Prefix for persistent, context-aware shortcut menus. @@ -168,7 +167,6 @@ pub struct TuiGlobalKeymap { /// Chat context keybindings. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiChatKeymap { /// Open saved prompts and slash commands. @@ -197,7 +195,6 @@ pub struct TuiChatKeymap { /// Composer context keybindings. These override corresponding `global` actions. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiComposerKeymap { /// Submit the current composer draft. @@ -214,7 +211,6 @@ pub struct TuiComposerKeymap { /// Editor context keybindings for text editing inside text areas. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiEditorKeymap { /// Insert a newline in the editor. @@ -388,7 +384,6 @@ pub struct TuiVimOperatorKeymap { /// Search motions shared by Vim normal and operator-pending input. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiVimSearchKeymap { /// Search forward in the active buffer (`/`). @@ -403,7 +398,6 @@ pub struct TuiVimSearchKeymap { /// Vim text-object keybindings for modal editing inside text areas. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiVimTextObjectKeymap { /// Text object: word. @@ -428,7 +422,6 @@ pub struct TuiVimTextObjectKeymap { /// Pager context keybindings for transcript and static overlays. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiPagerKeymap { /// Scroll up by one row. @@ -457,7 +450,6 @@ pub struct TuiPagerKeymap { /// List selection context keybindings for popup-style selectable lists. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiListKeymap { /// Move list selection up. @@ -484,7 +476,6 @@ pub struct TuiListKeymap { /// Shortcuts specific to the shared agents overview. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiAgentsKeymap { /// Open the session resume picker. @@ -515,7 +506,6 @@ pub struct TuiAgentsKeymap { /// Approval overlay keybindings. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiApprovalKeymap { /// Open the full-screen approval details view. @@ -547,7 +537,6 @@ pub struct TuiApprovalKeymap { /// `RuntimeKeymap` first so precedence, empty-list unbinding, and duplicate-key /// validation are applied consistently. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)] -#[serde(deny_unknown_fields)] #[schemars(deny_unknown_fields)] pub struct TuiKeymap { #[serde(default)] @@ -767,75 +756,6 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - #[test] - fn misplaced_action_at_keymap_root_is_rejected() { - // Actions placed directly under [tui.keymap] instead of a context - // sub-table (e.g. [tui.keymap.global]) must produce a parse error, - // not be silently ignored. - let toml_input = r#" - open_transcript = "ctrl-s" - "#; - let result = toml::from_str::(toml_input); - assert!( - result.is_err(), - "expected error for action at keymap root, got: {result:?}" - ); - } - - #[test] - fn misspelled_action_under_context_is_rejected() { - let toml_input = r#" - [global] - open_transcrip = "ctrl-x" - "#; - let err = toml::from_str::(toml_input) - .expect_err("expected unknown action under context"); - assert!( - err.to_string().contains("open_transcrip"), - "expected error to mention misspelled field, got: {err}" - ); - } - - #[test] - fn misspelled_vim_text_object_action_is_rejected() { - let toml_input = r#" - [vim_text_object] - double_quotes = "shift-quote" - "#; - let err = toml::from_str::(toml_input) - .expect_err("expected unknown vim text object action"); - assert!( - err.to_string().contains("double_quotes"), - "expected error to mention misspelled field, got: {err}" - ); - } - - #[test] - fn removed_backtrack_actions_are_rejected() { - for (context, action) in [ - ("global", "edit_previous_message"), - ("global", "confirm_edit_previous_message"), - ("chat", "edit_previous_message"), - ("chat", "confirm_edit_previous_message"), - ("pager", "edit_previous_message"), - ("pager", "edit_next_message"), - ("pager", "confirm_edit_message"), - ] { - let toml_input = format!( - r#" - [{context}] - {action} = "ctrl-x" - "# - ); - let err = toml::from_str::(&toml_input) - .expect_err("expected removed backtrack action to be rejected"); - assert!( - err.to_string().contains(action), - "expected error to mention removed field {action}, got: {err}" - ); - } - } - #[test] fn action_under_global_context_is_accepted() { let toml_input = r#" diff --git a/codex-rs/core/src/config/config_tests.rs b/codex-rs/core/src/config/config_tests.rs index 1799330505..5e74da2e70 100644 --- a/codex-rs/core/src/config/config_tests.rs +++ b/codex-rs/core/src/config/config_tests.rs @@ -4738,21 +4738,6 @@ async fn legacy_remote_thread_store_endpoint_is_rejected() { assert!(err.to_string().contains("no longer supported")); } -#[test] -fn profile_tui_rejects_unsupported_settings() { - let err = toml::from_str::( - r#"profile = "work" - -[profiles.work.tui] -theme = "dark" -"#, - ) - .expect_err("profile TUI config should only accept supported fields"); - - assert!(err.to_string().contains("unknown field")); - assert!(err.to_string().contains("theme")); -} - #[tokio::test] async fn runtime_config_resolves_session_picker_view_default_and_override() { let cfg = Config::load_from_base_config_with_overrides(