diff --git a/docs/config.md b/docs/config.md index 9d53538cae..10aff00467 100644 --- a/docs/config.md +++ b/docs/config.md @@ -6,6 +6,77 @@ For advanced configuration instructions, see [this documentation](https://develo For a full configuration reference, see [this documentation](https://developers.openai.com/codex/config-reference). +## TUI keymap + +The TUI supports rebinding shortcuts via `[tui.keymap]` in `~/.codex/config.toml`. + +Use this complete, commented defaults template. +Keymap template: https://github.com/openai/codex/blob/main/docs/default-keymap.toml +For implementation details, safety contracts, and testing notes, see `docs/tui-keymap.md`. + +### Precedence + +Precedence is applied in this order (highest first): + +1. Context-specific binding (`[tui.keymap.]`) +2. Global binding (`[tui.keymap.global]`) for chat/composer fallback actions +3. Built-in preset defaults (`preset`) + +### Presets + +- `latest`: moving alias for the newest preset; today `latest -> v1` +- `v1`: frozen legacy/current defaults + +When defaults change in the future, a new version (for example `v2`) is added and +`latest` may move to it. Pin to `v1` if you want stable historical behavior. + +### Supported actions + +- `global`: `open_transcript`, `open_external_editor`, `edit_previous_message`, + `confirm_edit_previous_message`, `submit`, `queue`, `toggle_shortcuts` +- `chat`: `edit_previous_message`, `confirm_edit_previous_message` +- `composer`: `submit`, `queue`, `toggle_shortcuts` +- `editor`: `insert_newline`, `move_left`, `move_right`, `move_up`, `move_down`, + `move_word_left`, `move_word_right`, `move_line_start`, `move_line_end`, + `delete_backward`, `delete_forward`, `delete_backward_word`, `delete_forward_word`, + `kill_line_start`, `kill_line_end`, `yank` +- `pager`: `scroll_up`, `scroll_down`, `page_up`, `page_down`, `half_page_up`, + `half_page_down`, `jump_top`, `jump_bottom`, `close`, `close_transcript`, + `edit_previous_message`, `edit_next_message`, `confirm_edit_message` +- `list`: `move_up`, `move_down`, `accept`, `cancel` +- `approval`: `open_fullscreen`, `approve`, `approve_for_session`, + `approve_for_prefix`, `decline`, `cancel` +- `onboarding`: `move_up`, `move_down`, `select_first`, `select_second`, + `select_third`, `confirm`, `cancel`, `quit`, `toggle_animation` + +For long-term behavior and evolution guidance, see `docs/tui-keymap.md`. +For a quick action inventory, see `docs/keymap-action-matrix.md`. +On onboarding API-key entry, printable `quit` bindings are treated as text input +once the field contains text; use control/alt chords for always-available quit +shortcuts. + +### Key format + +Use lowercase key identifiers with `-` separators, for example: + +- `ctrl-a` +- `shift-enter` +- `alt-page-down` +- `?` + +Actions accept a single key or multiple keys: + +- `submit = "enter"` +- `submit = ["enter", "ctrl-j"]` +- `submit = []` (explicitly unbind) + +Some defaults intentionally include multiple variants for one logical shortcut +because terminal modifier reporting can differ by platform/emulator. For +example, `?` may arrive as plain `?` or `shift-?`, and control chords may +arrive with or without `SHIFT`. + +Aliases like `escape`, `pageup`, and `pgdn` are normalized. + ## Connecting to MCP servers Codex can connect to MCP servers configured in `~/.codex/config.toml`. See the configuration reference for the latest MCP server options: diff --git a/docs/default-keymap.toml b/docs/default-keymap.toml new file mode 100644 index 0000000000..bd0575d6a3 --- /dev/null +++ b/docs/default-keymap.toml @@ -0,0 +1,116 @@ +# Codex TUI keymap template (v1 defaults) +# +# Copy the sections you need into ~/.codex/config.toml. +# +# Canonical source (Codex repo): +# https://github.com/openai/codex/blob/main/docs/default-keymap.toml +# Implementation reference: docs/tui-keymap.md +# +# Value format: +# - action = "ctrl-a" # one key +# - action = ["ctrl-a", "alt-a"] # multiple keys for one action +# - action = [] # explicit unbind for that action +# +# Precedence (highest first): +# 1. [tui.keymap.] override +# 2. [tui.keymap.global] fallback (only for chat/composer actions) +# 3. preset defaults + +[tui.keymap] +# Preset alias for built-in defaults. +# "latest" currently points to "v1". +preset = "latest" + +[tui.keymap.global] +# Open transcript overlay. +open_transcript = "ctrl-t" +# Open external editor for current draft. +open_external_editor = "ctrl-g" +# Begin/advance "edit previous message" when composer is empty. +edit_previous_message = "esc" +# Confirm selected message in edit-previous flow. +confirm_edit_previous_message = "enter" +# Submit current draft. +submit = "enter" +# Queue current draft while a task is running. +queue = "tab" +# Toggle composer shortcut overlay. +# Include both forms because some terminals report `?` with SHIFT and others don't. +toggle_shortcuts = ["?", "shift-?"] + +[tui.keymap.chat] +# Overrides [tui.keymap.global] for chat-only behavior. +edit_previous_message = "esc" +confirm_edit_previous_message = "enter" + +[tui.keymap.composer] +# Overrides [tui.keymap.global] for composer behavior. +submit = "enter" +queue = "tab" +toggle_shortcuts = ["?", "shift-?"] + +[tui.keymap.editor] +# Text input editor bindings. +insert_newline = ["ctrl-j", "ctrl-m", "enter", "shift-enter"] +move_left = ["left", "ctrl-b"] +move_right = ["right", "ctrl-f"] +move_up = ["up", "ctrl-p"] +move_down = ["down", "ctrl-n"] +move_word_left = ["alt-b", "alt-left", "ctrl-left"] +move_word_right = ["alt-f", "alt-right", "ctrl-right"] +move_line_start = ["home", "ctrl-a"] +move_line_end = ["end", "ctrl-e"] +delete_backward = ["backspace", "ctrl-h"] +delete_forward = ["delete", "ctrl-d"] +delete_backward_word = ["alt-backspace", "ctrl-w", "ctrl-alt-h"] +delete_forward_word = ["alt-delete"] +kill_line_start = ["ctrl-u"] +kill_line_end = ["ctrl-k"] +yank = ["ctrl-y"] + +[tui.keymap.pager] +# Transcript/static overlay navigation. +scroll_up = ["up", "k"] +scroll_down = ["down", "j"] +page_up = ["page-up", "shift-space", "ctrl-b"] +page_down = ["page-down", "space", "ctrl-f"] +half_page_up = ["ctrl-u"] +half_page_down = ["ctrl-d"] +jump_top = ["home"] +jump_bottom = ["end"] +close = ["q", "ctrl-c"] +close_transcript = ["ctrl-t"] +# Backtrack controls inside transcript overlay. +edit_previous_message = ["esc", "left"] +edit_next_message = ["right"] +confirm_edit_message = ["enter"] + +[tui.keymap.list] +# Generic list/picker navigation. +move_up = ["up", "ctrl-p", "k"] +move_down = ["down", "ctrl-n", "j"] +accept = ["enter"] +cancel = ["esc"] + +[tui.keymap.approval] +# Approval modal actions. +# Include ctrl+shift fallback for terminals that preserve SHIFT on Ctrl letter chords. +open_fullscreen = ["ctrl-a", "ctrl-shift-a"] +approve = ["y"] +approve_for_session = ["a"] +approve_for_prefix = ["p"] +decline = ["esc", "n"] +cancel = ["c"] + +[tui.keymap.onboarding] +# Onboarding/auth/trust screens. +move_up = ["up", "k"] +move_down = ["down", "j"] +select_first = ["1", "y"] +select_second = ["2", "n"] +select_third = ["3"] +confirm = ["enter"] +cancel = ["esc"] +quit = ["q", "ctrl-c", "ctrl-d"] +# Include ctrl+shift fallback for terminals that preserve SHIFT on Ctrl punctuation chords. +toggle_animation = ["ctrl-.", "ctrl-shift-."] diff --git a/docs/example-config.md b/docs/example-config.md index 84b11436c5..f1102b63ec 100644 --- a/docs/example-config.md +++ b/docs/example-config.md @@ -1,3 +1,42 @@ # Sample configuration -For a sample configuration file, see [this documentation](https://developers.openai.com/codex/config-sample). +For a full sample configuration, see [this documentation](https://developers.openai.com/codex/config-sample). + +## Keymap snippet + +```toml +[tui.keymap] +preset = "latest" # currently points to v1 defaults + +[tui.keymap.global] +open_transcript = "ctrl-t" +open_external_editor = "ctrl-g" + +[tui.keymap.chat] +edit_previous_message = "esc" +confirm_edit_previous_message = "enter" + +[tui.keymap.composer] +submit = "enter" +queue = "tab" +toggle_shortcuts = ["?", "shift-?"] + +[tui.keymap.pager] +close = ["q", "ctrl-c"] +close_transcript = ["ctrl-t"] + +[tui.keymap.list] +accept = "enter" +cancel = "esc" + +[tui.keymap.approval] +approve = "y" +decline = ["esc", "n"] + +[tui.keymap.onboarding] +quit = ["q", "ctrl-c", "ctrl-d"] +``` + +For a complete, commented template: +Keymap template: https://github.com/openai/codex/blob/main/docs/default-keymap.toml +For precedence, safety invariants, and testing notes, see `docs/tui-keymap.md`. diff --git a/docs/keymap-action-matrix.md b/docs/keymap-action-matrix.md new file mode 100644 index 0000000000..ee28c807fe --- /dev/null +++ b/docs/keymap-action-matrix.md @@ -0,0 +1,101 @@ +# TUI Keymap Action Matrix + +This file defines the supported keymap actions and their default `v1` bindings. +For runtime behavior, safety invariants, and testing guidance, see +`docs/tui-keymap.md`. + +## Preset behavior + +- `latest` is an alias to the newest shipped preset. +- Today, `latest -> v1`. +- To keep stable behavior over time, pin `preset = "v1"`. + +## Precedence + +1. `tui.keymap..` +2. `tui.keymap.global.` (chat/composer fallback actions only) +3. Preset default (`v1` today) + +## Default `v1` Compatibility Notes + +- Some actions intentionally ship with multiple bindings for the same logical + shortcut because terminals differ in modifier reporting. +- Today this includes: + - `composer.toggle_shortcuts`: `?` and `shift-?` + - `approval.open_fullscreen`: `ctrl-a` and `ctrl-shift-a` + - `onboarding.toggle_animation`: `ctrl-.` and `ctrl-shift-.` +- Keep these paired defaults unless/until key-event normalization is made + platform-consistent at a lower layer. + +## Action Definitions + +### `global` + +- `open_transcript`: open transcript overlay +- `open_external_editor`: open external editor for current draft +- `edit_previous_message`: begin/advance edit-previous flow when composer is empty +- `confirm_edit_previous_message`: confirm selected previous message for editing +- `submit`: submit current draft +- `queue`: queue current draft while a task is running +- `toggle_shortcuts`: toggle composer shortcut overlay + +### `chat` + +- `edit_previous_message`: chat override for edit-previous flow +- `confirm_edit_previous_message`: chat override for edit confirmation + +### `composer` + +- `submit`: composer override for submit +- `queue`: composer override for queue +- `toggle_shortcuts`: composer override for shortcut overlay toggle + +### `editor` + +- `insert_newline`: insert newline in text editor +- `move_left` / `move_right` / `move_up` / `move_down`: cursor movement +- `move_word_left` / `move_word_right`: word movement +- `move_line_start` / `move_line_end`: line boundary movement +- `delete_backward` / `delete_forward`: single-char deletion +- `delete_backward_word` / `delete_forward_word`: word deletion +- `kill_line_start` / `kill_line_end`: kill to line boundary +- `yank`: paste kill-buffer contents + +### `pager` + +- `scroll_up` / `scroll_down`: row scroll +- `page_up` / `page_down`: page scroll +- `half_page_up` / `half_page_down`: half-page scroll +- `jump_top` / `jump_bottom`: jump to top/bottom +- `close`: close pager overlay +- `close_transcript`: close transcript via transcript toggle binding +- `edit_previous_message` / `edit_next_message`: backtrack navigation in transcript +- `confirm_edit_message`: confirm selected backtrack message + +### `list` + +- `move_up` / `move_down`: list navigation +- `accept`: select current item +- `cancel`: close list view + +### `approval` + +- `open_fullscreen`: open full-screen approval details +- `approve`: approve primary request +- `approve_for_session`: approve-for-session option +- `approve_for_prefix`: approve-for-prefix option +- `decline`: decline request +- `cancel`: cancel elicitation request +- MCP elicitation safety rule: `Esc` is always treated as `cancel` (never + `decline`) so dismissal cannot accidentally continue execution. + +### `onboarding` + +- `move_up` / `move_down`: onboarding list navigation +- `select_first` / `select_second` / `select_third`: numeric selection shortcuts +- `confirm`: confirm highlighted onboarding selection +- `cancel`: cancel current onboarding sub-flow +- `quit`: quit onboarding flow +- `toggle_animation`: switch welcome animation variant +- API-key entry guard: printable `quit` bindings are treated as text input once + the API-key field has text; control/alt quit chords are not suppressed. diff --git a/docs/keymap-rollout-plan.md b/docs/keymap-rollout-plan.md new file mode 100644 index 0000000000..2239028018 --- /dev/null +++ b/docs/keymap-rollout-plan.md @@ -0,0 +1,69 @@ +# TUI Keymap Additive Rollout Plan + +This plan restructures the existing keymap implementation into a reviewable +additive stack while preserving the final behavior from revision `0f03e76c`. + +## Goals + +- Keep the final behavior effectively identical to `0f03e76c`. +- Reorder history to show intent and risk reduction clearly. +- Make each change compile, format cleanly, and pass scoped tests. +- Establish high-confidence behavior characterization before binding rewrites. + +## Commit Sequence + +1. **Docs and specification foundation** + - Add/reshape long-term docs that explain how the keymap system works. + - Include: + - conceptual model + - action matrix + - default keymap template guidance with explicit URL + - testing strategy notes + - No functional code change. + +2. **Additive config primitives (not wired)** + - Introduce keymap config types and schema support in `core`. + - Keep runtime behavior unchanged by not consuming these config values yet. + - Ensure parsing/serialization paths are complete and documented. + +3. **Behavior characterization tests (pre-rewrite)** + - Add tests that lock down existing event behavior before switching bindings. + - Cover key event matching and context-sensitive behaviors that must stay stable. + - Use these tests as the safety net for subsequent rewiring. + - Run `cargo llvm-cov` and target full branch coverage for keybinding logic. + +4. **Binding replacement using characterized behavior** + - Introduce keymap-driven binding resolution and wire call sites. + - Replace legacy binding checks while preserving characterized behavior. + - Keep docs in sync with any semantics that became explicit. + +5. **UX polish and affordances** + - Apply help/footer/tooltip/key-hint refinements once behavior is stable. + - Keep affordance text concise and formatted for readability. + - Update snapshots as needed. + +## Validation Gates Per Commit + +For each commit in the sequence: + +1. Run formatting (`just fmt` in `codex-rs`). +2. Run scoped tests for affected crates (minimum `cargo test -p codex-tui` for TUI + changes and relevant `core` tests when `core` changes). +3. Ensure branch compiles cleanly (`cargo test` path used as compile gate). +4. For characterization and binding commits, run coverage and track branch + coverage for keybinding logic (`cargo llvm-cov`). + +## JJ Workflow + +- Build stack from `trunk()` with explicit ordered changes. +- Use `jj new -A` / `jj new -B` to place changes relative to target revisions. +- Rebase existing implementation changes into the new stack as needed. +- Resolve conflicts immediately and keep each change coherent. +- Use clear `jj describe` messages with title + explanatory body. + +## Acceptance Criteria + +- Final tip behavior matches `0f03e76c` in practice. +- Stack reads top-to-bottom as: docs -> config -> characterization -> rewiring -> + UX polish. +- Each commit is independently reviewable with passing checks. diff --git a/docs/tui-keymap.md b/docs/tui-keymap.md new file mode 100644 index 0000000000..949d285dfd --- /dev/null +++ b/docs/tui-keymap.md @@ -0,0 +1,277 @@ +# TUI Keymap Implementation Reference + +This document is the long-term implementation reference for Codex TUI keybindings. +It describes how keymap configuration is resolved at runtime, which safety +contracts are intentionally strict, and how to test behavior end to end. + +## Scope and boundaries + +This keymap system is action-based and context-aware. It supports user rebinding +for the TUI without requiring source code edits. + +Responsibilities: + +1. Resolve config values into runtime key bindings. +2. Apply deterministic precedence. +3. Reject ambiguous bindings in dispatch scopes where collisions are unsafe. +4. Preserve explicit safety semantics for approval elicitation and onboarding. + +Non-responsibilities: + +1. It does not choose which screen should handle an event. +2. It does not persist config. +3. It does not guarantee terminal modifier reporting consistency; defaults may + include compatibility variants for that reason. + +## Source-of-truth map + +- Runtime resolution: `codex-rs/tui/src/keymap.rs` +- Onboarding flow-level routing and quit guard: + `codex-rs/tui/src/onboarding/onboarding_screen.rs` +- Approval/MCP elicitation option semantics: + `codex-rs/tui/src/bottom_pane/approval_overlay.rs` +- Generic list popup navigation semantics: + `codex-rs/tui/src/bottom_pane/list_selection_view.rs` +- User-facing default template: + `https://github.com/openai/codex/blob/main/docs/default-keymap.toml` +- User-facing config overview: `docs/config.md` + +## Config contract + +`[tui.keymap]` is action-to-binding mapping by context. + +```toml +[tui.keymap] +preset = "latest" + +[tui.keymap.global] +submit = "enter" + +[tui.keymap.composer] +submit = ["enter", "ctrl-j"] +``` + +Rules: + +1. Mapping direction is `action -> key_or_keys`. +2. Values support: + 1. `action = "key-spec"` + 2. `action = ["key-spec-1", "key-spec-2"]` + 3. `action = []` for explicit unbind. +3. Unknown contexts, actions, or key identifiers fail validation. +4. Aliases are normalized (for example `escape -> esc`, `pgdn -> page-down`). +5. Key identifiers are lowercase and use `-` separators. + +## Contexts and actions + +Supported contexts: + +1. `global` +2. `chat` +3. `composer` +4. `editor` +5. `pager` +6. `list` +7. `approval` +8. `onboarding` + +Action inventory by context is documented in `docs/config.md` and the template +at `https://github.com/openai/codex/blob/main/docs/default-keymap.toml`. + +## Presets and compatibility policy + +Preset semantics: + +1. `latest` is an alias to the newest shipped preset. +2. `v1` is the current frozen baseline. +3. Today, `latest -> v1`. + +User guidance: + +1. Pin `preset = "v1"` for stable behavior over time. +2. Use `preset = "latest"` to adopt new defaults when `latest` moves. + +Developer policy: + +1. Do not mutate old preset defaults after release. +2. Add a new version (for example `v2`) for behavior changes. +3. Update docs and migration notes whenever `latest` changes. + +Compatibility detail: + +Some actions intentionally ship with multiple default bindings because terminals +can report modifier combinations differently. Examples include `?` vs `shift-?` +and certain `ctrl` chords with optional `shift`. + +## Resolution and precedence + +Resolution order (highest first): + +1. Context binding (`tui.keymap..`) +2. Global fallback (`tui.keymap.global.`) for chat/composer fallback + actions only +3. Preset default binding + +If no binding matches, normal unhandled-key fallback behavior applies. + +## Conflict validation model + +Validation is dispatch-order aware, not globally uniform. + +Current conflict passes in `RuntimeKeymap::validate_conflicts` enforce: + +1. App-level uniqueness for app actions and app-level chat controls. +2. App/composer shadowing prevention, because app handlers execute before + forwarding to composer handlers. +3. Composer-local uniqueness for submit/queue/shortcut-toggle. +4. Context-local uniqueness in editor, pager, list, approval, and onboarding. + +Intentionally allowed: + +1. Same key across different contexts that are not co-evaluated in a way that + can cause unsafe shadowing. +2. Shared defaults where runtime context gating keeps semantics unambiguous. + +## Safety invariants + +### MCP elicitation cancel semantics + +For MCP elicitation prompts, `Esc` is always treated as `cancel`. + +Implementation contract: + +1. `Esc` is always included in cancel shortcuts. +2. User-defined `approval.cancel` shortcuts are merged into cancel. +3. Any overlap is removed from `approval.decline` in elicitation mode. + +Rationale: dismissal must remain a safe abort path and never silently map to +"continue without requested info". + +### Onboarding API-key text-entry guard + +During API-key entry, printable `onboarding.quit` bindings are suppressed only +when the API-key field already has text. + +Implementation contract: + +1. Guard applies only when API-key entry mode is active. +2. Guard applies only to printable char keys without control/alt modifiers. +3. Guard applies only when input is non-empty. +4. Control/alt quit chords are never suppressed by this guard. + +Rationale: keep text-entry safe once typing has begun while preserving an +intentional printable-quit path on empty input. + +## Dispatch model and handler boundaries + +High-level behavior: + +1. App-level event handling runs before some lower-level handlers. +2. Composer behavior depends on both app routing and composer-local checks. +3. Onboarding screen routing applies flow-level rules before delegating to step + widgets. +4. Approval overlay and list selection use context-specific bindings resolved by + `RuntimeKeymap`. + +When changing dispatch order, re-evaluate conflict validation scopes in +`keymap.rs` and associated tests. + +## Diagnostics contract + +Validation errors should be actionable and include: + +1. Problem summary. +2. Exact config path. +3. Why the value is invalid or ambiguous. +4. Concrete remediation step. + +Categories currently covered: + +1. Invalid key specification. +2. Unknown action/context mapping. +3. Same-scope ambiguity. +4. Shadowing collisions in dispatch-coupled scopes. + +## Debug path + +When keybindings do not behave as expected, trace in this order: + +1. Verify config normalization and schema validation in + `codex-rs/core/src/config/tui_keymap.rs`. +2. Verify resolved runtime bindings and conflict checks in + `codex-rs/tui/src/keymap.rs` (`from_config`, `validate_conflicts`). +3. Verify handler-level dispatch order in: + 1. `codex-rs/tui/src/app.rs` for app/chat/composer routing. + 2. `codex-rs/tui/src/pager_overlay.rs` for pager controls. + 3. `codex-rs/tui/src/bottom_pane/approval_overlay.rs` for approval safety + behavior. + 4. `codex-rs/tui/src/onboarding/onboarding_screen.rs` for onboarding quit + guard behavior. +4. Reproduce with explicit bindings in `~/.codex/config.toml` and compare + against: + 1. `docs/default-keymap.toml` + 2. `docs/keymap-action-matrix.md` + +## Testing notes + +### Commands + +Run from `codex-rs/`: + +1. `just fmt` +2. `cargo test -p codex-tui --lib` +3. Optional full crate run (includes integration tests): + `cargo test -p codex-tui` +4. Optional focused runs while iterating: + `cargo test -p codex-tui --lib keymap::tests` + +If `cargo test -p codex-tui` fails because the `codex` binary cannot be found +in local `target/`, run `--lib` for keymap behavior checks and then validate the +integration target in an environment where workspace binaries are available. + +For intentional UI/text output changes in `codex-tui`: + +1. `cargo insta pending-snapshots -p codex-tui` +2. `cargo insta show -p codex-tui ` +3. `cargo insta accept -p codex-tui` only when the full snapshot set is + expected. + +### Behavior coverage checklist + +Use this checklist before landing keymap behavior changes: + +1. Precedence: context override beats global fallback and preset defaults. +2. Unbind behavior: `action = []` actually removes the binding. +3. Conflict rejection: + 1. Same-context duplicates fail. + 2. App/composer shadowing fails for submit, queue, and toggle-shortcuts. +4. Approval safety: + 1. `Esc` resolves elicitation to cancel. + 2. Decline shortcuts never contain cancel overlaps in elicitation mode. +5. Onboarding safety: + 1. Printable quit key is suppressed when API-key input is active and + non-empty. + 2. Printable quit key is not suppressed when input is empty. + 3. Control/alt quit chords are not suppressed. +6. Footer/help hints continue to reflect effective primary bindings. +7. `https://github.com/openai/codex/blob/main/docs/default-keymap.toml`, + `docs/config.md`, and `docs/example-config.md` stay aligned with runtime + action names and defaults. + +### Manual sanity checks + +1. Start onboarding and enter API-key mode. +2. Bind `onboarding.quit` to a printable key. +3. Verify that key quits when input is empty, then types once text exists. +4. Verify `ctrl-c` or another control quit chord still exits. +5. Trigger an MCP elicitation request and verify `Esc` cancels, not declines. + +## Documentation maintenance + +When adding/changing keymap API surface: + +1. Update runtime definitions and defaults in `codex-rs/tui/src/keymap.rs`. +2. Update `docs/default-keymap.toml`. +3. Update `docs/config.md` and `docs/example-config.md` snippets. +4. Update this file with behavioral or safety contract changes. +5. Add/update regression tests in `codex-rs/tui`.