mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
edc48e4612b457f2e6a32a007516fbde6fc5a832
22 Commits
-
Sync TUI thread settings through app server (#23507)
Builds on #23502. ## Why #23502 adds the app-server `thread/settings/update` API and matching `thread/settings/updated` notification. The TUI already lets users change thread-scoped settings such as model, reasoning effort, service tier, approvals, permissions, personality, and collaboration mode, but those updates need to flow through the app server so embedded and connected clients observe the same thread state. This is a rework (simplification) of PR https://github.com/openai/codex/pull/22510. It has the same functionality, but the underlying `thread/settings/update` api is now simpler in that it no longer returns the effective settings as a response. Now, clients receive the effective settings only through the `thread/settings/updated` notification. ## What Changed This updates the TUI to send `thread/settings/update` whenever those thread-scoped settings change and to treat the RPC response as the authoritative acknowledgement. It also routes `thread/settings/updated` notifications back into cached session state and the visible chat widget so active and inactive threads stay in sync after app-server-originated changes. The implementation is kept to the TUI layer: settings conversion and merge logic live under `codex-rs/tui/src/app/thread_settings.rs`, with dispatch/routing hooks in the existing app and chat widget paths. ## Verification I manually tested using `codex app-server --listen unix://` and then launching two copies of the TUI that use the same local app server. I then resumed the same thread on both and verified that changes like plan mode, fast mode, model, reasoning effort, etc. are reflected "live" in the second client when modified in the first and vice versa.
Eric Traut ·
2026-05-20 11:05:14 -07:00 -
Fix stale background terminal poll events (#23231)
## Why Issue #23214 reports `/ps` showing no background terminals while the status line still says it is waiting for a background terminal. The race is in core: `write_stdin` can poll a process that exits before the response returns. The process manager correctly returns `process_id: None`, but the handler still emitted a `TerminalInteraction` event using the requested session id, causing clients to believe a dead process was still being polled. Fixes #23214. ## What changed - Suppress `TerminalInteraction` events for empty `write_stdin` polls once `response.process_id` is `None`. - Continue emitting interactions for non-empty stdin, even if that input causes the process to exit before the response returns. - Extend the unified exec integration test to assert completed empty polls do not emit terminal interactions. ## Verification - `cargo test -p codex-core --test all unified_exec_emits_one_begin_and_one_end_event` - `cargo test -p codex-core --test all unified_exec_emits_terminal_interaction_for_write_stdin` `cargo test -p codex-core` currently aborts in unrelated `agent::control::tests::resume_agent_from_rollout_uses_edge_data_when_descendant_metadata_source_is_stale` with a reproducible stack overflow.
Eric Traut ·
2026-05-19 20:48:37 -07:00 -
app-server: use permission ids and runtime workspace roots (#22611)
## Why This PR builds on [#22610](https://github.com/openai/codex/pull/22610) and is the app-server side of the migration from mutable per-turn `SandboxPolicy` replacement toward selecting immutable permission profiles by id plus mutable runtime workspace roots. Once permission profiles can carry their own immutable `workspace_roots`, app-server no longer needs to mutate the selected `PermissionProfile` just to represent thread-specific filesystem context. The mutable part now lives on the thread as explicit `runtimeWorkspaceRoots`, while `:workspace_roots` remains symbolic until the sandbox is realized for a turn. ## What Changed - Replaced the v2 permission-selection wrapper surface with plain profile ids for `thread/start`, `thread/resume`, `thread/fork`, and `turn/start`. - Removed the API surface for profile modifications (`PermissionProfileSelectionParams`, `PermissionProfileModificationParams`, `ActivePermissionProfileModification`). - Added experimental `runtimeWorkspaceRoots` fields to the thread lifecycle and turn-start APIs. - Threaded runtime workspace roots through core session/thread snapshots, turn overrides, app-server request handling, and command execution permission resolution. - Kept session permission state symbolic so later runtime root updates and cwd-only implicit-root retargeting rebind `:workspace_roots` correctly. - Updated the embedded clients just enough to send and restore the new thread state. - Refreshed the generated schema/TypeScript artifacts and the app-server README to match the new contract. ## Verification Targeted coverage for this layer lives in: - `codex-rs/app-server-protocol/src/protocol/v2/tests.rs` - `codex-rs/app-server/tests/suite/v2/thread_start.rs` - `codex-rs/app-server/tests/suite/v2/thread_resume.rs` - `codex-rs/app-server/tests/suite/v2/turn_start.rs` - `codex-rs/core/src/session/tests.rs` The key regression checks exercise that: - `runtimeWorkspaceRoots` resolve against the effective cwd on thread start. - Profile-declared workspace roots are excluded from the runtime workspace roots returned by app-server. - A turn-level runtime workspace-root update persists onto the thread and is returned by `thread/resume`. - A named permission profile selected on one turn remains symbolic so a later runtime-root-only turn update changes the actual sandbox writes. - A cwd-only turn update retargets the implicit runtime cwd root while preserving additional runtime roots. - The protocol fixtures and generated client artifacts stay in sync with the string-based permission selection contract. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22611). * #22612 * __->__ #22611
Michael Bolin ·
2026-05-14 23:00:05 -07:00 -
Split ChatWidget state into focused modules (#21866)
## Summary `ChatWidget` has been carrying several independent domains in one large state bag: transcript bookkeeping, turn lifecycle, queued input, status surfaces, connectors, review mode, and protocol dispatch. That makes otherwise-local changes hard to reason about because unrelated fields and side effects live beside each other in `chatwidget.rs`. This is the first cleanup PR in a larger decomposition effort. It does not try to make `chatwidget.rs` small in one sweep; instead, it establishes focused state boundaries that later handler, popup, rendering, and effect-synchronization extractions can build on. This PR keeps `ChatWidget` as the composition layer while moving focused state into smaller `codex-tui` modules. The widget still owns effects that touch the bottom pane, app events, command submission, redraw scheduling, and terminal-title updates. ## Changes - Add focused state modules under `codex-rs/tui/src/chatwidget/` for input queues, turn lifecycle, transcript bookkeeping, status state, connectors, review mode, and app-server protocol dispatch. - Update `ChatWidget` to hold grouped state structs and route input/lifecycle/status operations through those focused helpers. - Move app-server notification dispatch into `chatwidget/protocol.rs` while leaving feature handlers and side effects on `ChatWidget`. - Replace the large manual `ChatWidget` test literal with the normal constructor plus narrow test overrides, so future state moves do not require every field to be restated in test setup. - Update existing tests to access the new grouped state or narrower helpers without changing snapshot behavior. ## Longer-term direction Follow-up PRs can continue shrinking `chatwidget.rs` by moving behavior, not just state, into focused modules: - Extract input/submission flow, turn/stream handling, and tool-cell lifecycles into domain modules that call the new state reducers. - Move popup/settings builders and rendering helpers out of the main widget file so `ChatWidget` stays focused on composition. - Reduce direct `BottomPane` mutation by applying domain-specific sync outputs at clearer boundaries.
Eric Traut ·
2026-05-09 15:16:01 -07:00 -
[codex-analytics] plumb protocol-native review timing (#21434)
## Why We want terminal tool review analytics, but the reducer should not stamp review timing from its own wall clock. This PR plumbs review timing through the real protocol and app-server seams so downstream analytics can consume the emitter's timestamps directly. Guardian reviews keep their enriched `started_at` / `completed_at` analytics fields by deriving those legacy second-based values from the same protocol-native millisecond lifecycle timestamps, rather than sampling a separate analytics clock. ## What changed - add `started_at_ms` to user approval request payloads - add `started_at_ms` / `completed_at_ms` to guardian review notifications - preserve Guardian review `started_at` / `completed_at` enrichment from the protocol-native timing source - stamp typed `ServerResponse` analytics facts with app-server-observed `completed_at_ms` - thread the new timing fields through core, protocol, app-server, TUI, and analytics fixtures ## Verification - `cargo test -p codex-app-server outgoing_message --manifest-path codex-rs/Cargo.toml` - `cargo test -p codex-app-server-protocol guardian --manifest-path codex-rs/Cargo.toml` - `cargo test -p codex-tui guardian --manifest-path codex-rs/Cargo.toml` - `cargo test -p codex-analytics analytics_client_tests --manifest-path codex-rs/Cargo.toml` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/21434). * #18748 * __->__ #21434 * #18747 * #17090 * #17089 * #20514
rhan-oai ·
2026-05-07 20:31:41 -07:00 -
Move message history out of core (#21278)
## Why Message history was implemented inside `codex-core` and surfaced through core protocol ops and `SessionConfiguredEvent` fields even though the current consumer is TUI-local prompt recall. That made core own UI history persistence and exposed `history_log_id` / `history_entry_count` through surfaces that app-server and other clients do not need. This change moves message history persistence out of core and keeps the recall plumbing local to the TUI. ## What changed - Added a new `codex-message-history` crate for appending, looking up, trimming, and reading metadata from `history.jsonl`. - Removed core protocol history ops/events: `AddToHistory`, `GetHistoryEntryRequest`, and `GetHistoryEntryResponse`. - Removed `history_log_id` and `history_entry_count` from `SessionConfiguredEvent` and updated exec/MCP/test fixtures accordingly. - Updated the TUI to dispatch local app events for message-history append/lookup and keep its persistent-history metadata in TUI session state. ## Validation - `cargo test -p codex-message-history -p codex-protocol` - `cargo test -p codex-exec event_processor_with_json_output` - `cargo test -p codex-mcp-server outgoing_message` - `cargo test -p codex-tui` - `just fix -p codex-message-history -p codex-protocol -p codex-core -p codex-tui -p codex-exec -p codex-mcp-server`
pakrym-oai ·
2026-05-06 08:35:42 -07:00 -
feat: add
session_id(#20437)## Summary Related to https://openai.slack.com/archives/C095U48JNL9/p1777537279707449 TLDR: We update the meaning of session ids and thread ids: * thread_id stays as now * session_id become a shared id between every thread under a /root thread (i.e. every sub-agent share the same session id) This PR introduces an explicit `SessionId` and threads it through the protocol/client boundary so `session_id` and `thread_id` can diverge when they need to, while preserving compatibility for older serialized `session_configured` events. --------- Co-authored-by: Codex <noreply@openai.com>
jif-oai ·
2026-05-06 10:48:37 +02:00 -
Remove core protocol dependency [2/2] (#20325)
## Why With the local model layer and app-server routing in place from PR1, this PR moves the active TUI runtime onto app-server notifications. The affected pieces share the same event flow, so the command surface, session state, bottom-pane prompts, chat rendering, history/status views, and tests move together to keep the stacked branch buildable. This PR also removes the obsolete compatibility surface that is no longer used after the migration. The proposed protocol-boundary verifier layer was dropped from the stack; enforcing that final boundary will be simpler once `codex-tui` no longer needs any `codex_protocol` references. This PR is part 2 of a 2-PR stack: 1. Add TUI-owned replacement models and extract app-server event routing. 2. Move the active TUI flow to app-server notifications and delete obsolete adapter code. ## What changed - Rewired app command and session handling to use app-server request and notification shapes. - Moved approval overlays, request-user-input flows, MCP elicitation, realtime events, and review commands onto the app-server-facing model surface. - Updated chat rendering, history cells, status views, multi-agent UI, replay state, and TUI tests to use app-server notifications plus the local models introduced in PR1. - Deleted `codex-rs/tui/src/app/app_server_adapter.rs` and the superseded `chatwidget/tests/background_events.rs` fixture path. ## Verification - `cargo check -p codex-tui --tests` - Top of stack: `cargo test -p codex-tui`
Eric Traut ·
2026-04-30 11:34:34 -07:00 -
Michael Bolin ·
2026-04-29 20:54:59 -07:00 -
TUI: Remove core protocol dependency [3/7] (#20174)
## Why This is part 3 of a 7-PR stack to remove direct `codex_protocol::protocol` usage from `codex-tui` while keeping each layer reviewable and shippable. With `AppCommand` now explicit, the internal app event bus can carry TUI commands directly instead of bouncing through core `Op` values. ## What changed - Changed `AppEvent::CodexOp` and `AppEvent::SubmitThreadOp` to carry `AppCommand`. - Updated app-event senders and direct emitters to submit `AppCommand` values. - Adjusted tests to match `AppCommand` or convert back through `into_core()` where they intentionally assert legacy payload equality. ## Verification - `cargo test -p codex-tui --no-run`
Eric Traut ·
2026-04-29 10:52:10 -07:00 -
TUI: use cumulative turn duration for worked-for separator (#19929)
## Why Fixes #19814. The TUI's current `Worked for ...` timing behavior is a leftover from #9599. At that point, models could emit multiple assistant messages in one turn for preambles/commentary, but the TUI did not yet have a reliable signal that an assistant message was the final answer when it started streaming. To avoid showing an ever-growing elapsed time on each preamble separator, #9599 made the separator timer incremental by tracking elapsed time since the previous separator. That workaround is no longer the right model for the final completed-turn display. Since then, #16638 added protocol-native turn timing, including `duration_ms` on turn completion. With that cumulative duration available at the point where the TUI renders the completed-turn separator, the UI can show the actual turn duration directly instead of carrying per-separator timing state. ## What Changed - Thread `duration_ms` into `ChatWidget::on_task_complete` from both legacy `TurnCompleteEvent` handling and app-server `TurnCompleted` notifications. - Use `duration_ms` for the final `Worked for ...` separator, falling back to the status indicator timer only when the protocol duration is unavailable. - Keep mid-turn separators before later assistant text as plain visual dividers instead of clocked `Worked for ...` separators. - Remove the old incremental separator timer state and helper (`last_separator_elapsed_secs` / `worked_elapsed_from`). - Add a snapshot regression test for a turn that runs a command and then completes with a final answer, verifying the final separator uses the cumulative turn duration. ## Verification - `cargo test -p codex-tui final_worked_for_uses_cumulative_turn_duration_snapshot` - `just fix -p codex-tui` Manual repro prompt: ```text Manual timing repro. First send a short preamble/commentary sentence before using tools. Then run exactly this shell command: sleep 75; echo MANUAL_TIMING_DONE. After the command finishes, give a final answer that says "done". Do not skip the preamble. ``` After this change, the mid-turn break before the final answer should be a plain divider, and the final completed-turn separator should show `Worked for ...` using the cumulative turn duration. Before: <img width="414" height="102" alt="Screenshot 2026-04-27 at 10 09 01 PM" src="https://github.com/user-attachments/assets/b9e2ce01-2460-40e4-a5c4-c9ba8add2557" /> After: <img width="485" height="149" alt="Screenshot 2026-04-27 at 10 09 07 PM" src="https://github.com/user-attachments/assets/d24089ae-d4e2-41b6-b966-07c98706ead4" />
Eric Traut ·
2026-04-28 09:24:29 -07:00 -
permissions: make SessionConfigured profile-only (#19774)
## Why `SessionConfiguredEvent` is the internal event that tells clients what permissions are active for a session. Emitting both `sandbox_policy` and `permission_profile` leaves two possible authorities and forces every consumer to decide which one to honor. At this point in the migration, the profile is expressive enough to represent managed, disabled, and external sandbox enforcement, so the internal event can be profile-only. The wire compatibility concern is older serialized events or rollout data that only contain `sandbox_policy`; those still need to deserialize. ## What Changed - Removes `sandbox_policy` from `SessionConfiguredEvent` and makes `permission_profile` required. - Adds custom deserialization so old payloads with only `sandbox_policy` are upgraded to a cwd-anchored `PermissionProfile`. - Updates core event emission and TUI session handling to sync permissions from the profile directly. - Updates app-server response construction to derive the legacy `sandbox` response field from the active thread snapshot instead of from `SessionConfiguredEvent`. - Updates yolo-mode display logic to treat both `PermissionProfile::Disabled` and managed unrestricted filesystem plus enabled network as full-access, while still preserving the distinction between no sandbox and external sandboxing. ## Verification - `cargo test -p codex-protocol session_configured_event --lib` - `cargo test -p codex-protocol serialize_event --lib` - `cargo test -p codex-exec session_configured --lib` - `cargo test -p codex-app-server thread_response_permission_profile_preserves_enforcement --lib` - `cargo test -p codex-core session_configured_reports_permission_profile_for_external_sandbox --lib` - `cargo test -p codex-tui session_configured --lib` - `cargo test -p codex-tui yolo_mode_includes_managed_full_access_profiles --lib` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19774). * #19900 * #19899 * #19776 * #19775 * __->__ #19774
Michael Bolin ·
2026-04-27 22:06:47 -07:00 -
Render delegated patch approval details (#19709)
## Why Fixes #19632. When a delegated agent requests approval for an in-progress file change, the parent TUI handles that request from an inactive thread. The app server already sent the `FileChange` item with the proposed diff, but the inactive-thread approval path was not recovering and rendering it the same way as the active-thread path. The result was an inconsistent approval prompt: main-thread edits show a normal patch preview history item before the approval modal, while delegated edits did not show that preview in the transcript flow. ## What Changed - Recover buffered or historical `FileChange` item changes when building inactive-thread file-change approval requests. - Reuse the app-server file-change conversion helper for both live transcript rendering and inactive-thread approvals. - Render recovered delegated patches as a normal patch preview history cell before the approval modal. - Keep apply-patch approval modals focused on the decision prompt and optional metadata; they do not render a synthetic command line or embed the diff body. ## Manual Repro And Verification I manually reproduced the issue using a file under `~/Desktop` so the write would require approval. Before the fix: 1. Ask the main thread: `Use apply_patch, not shell redirection or Python, to create ~/Desktop/bug1.txt with three short lines.` 2. Observe the expected TUI shape: the transcript shows a normal patch preview such as `• Added ~/Desktop/bug1.txt (+N -0)` above the approval modal, and the modal contains only the approval prompt/options without a synthetic command line. 3. Ask for the delegated path: `Spawn a worker. Have it use apply_patch, not shell redirection or Python, to create ~/Desktop/bug1.txt with four short lines.` 4. Observe the delegated approval is inconsistent: the parent view does not render the proposed patch as the normal transcript preview before the modal, so the diff context is missing from the stream or appears inside the modal instead of in the history flow. After the fix: 1. Repeat the delegated worker prompt with `apply_patch`. 2. Confirm the parent view renders the same normal patch preview history cell (`• Added ~/Desktop/bug1.txt (+N -0)` plus the diff) immediately before the approval modal. 3. Confirm the approval modal remains focused on the decision prompt. For delegated approvals it may show the worker thread label, but it should not show a `$ apply_patch` command line or embed the diff body in the modal.
Eric Traut ·
2026-04-27 10:07:15 -07:00 -
Persist shell mode commands in prompt history (#19618)
## Why `!` shell commands are currently surfaced as "Bash mode", which is misleading for users running shells such as PowerShell or zsh. Those commands also bypass the persistent prompt history path, so they cannot be recalled after starting a new session. Fixes #19613. ## What changed - Rename the TUI footer label and related test wording from "Bash mode" to "Shell mode". - Persist accepted `!` shell commands to prompt history with the leading `!`, so recall restores the composer into shell mode across sessions. - Add coverage for immediate and queued shell-command submissions emitting the prompt-history update. ## Verification - `cargo test -p codex-tui bang_shell` - `cargo test -p codex-tui shell_command_uses_shell_accent_style` - `cargo test -p codex-tui footer_mode_snapshots` - `cargo insta pending-snapshots --manifest-path tui/Cargo.toml` Manually verified fix after confirming presence of bug prior to fix.
Eric Traut ·
2026-04-27 09:54:25 -07:00 -
protocol: report session permission profiles (#18282)
## Why Clients that observe `SessionConfigured` need the same canonical permission view that app-server thread responses provide. Reporting the profile in protocol events lets clients keep their local state synchronized without reinterpreting legacy sandbox fields. ## What changed This adds `permission_profile` to `SessionConfigured` and propagates it through core, exec JSON output, MCP server messages, and TUI history/widget handling. ## Verification - `cargo test -p codex-tui permissions -- --nocapture` - `cargo test -p codex-core --test all permissions_messages -- --nocapture` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18282). * #18288 * #18287 * #18286 * #18285 * #18284 * #18283 * __->__ #18282
Michael Bolin ·
2026-04-22 21:29:32 -07:00 -
[codex-analytics] guardian review TTFT plumbing and emission (#17696)
## Why Guardian analytics includes time-to-first-token, but the Guardian reviewer runs as a normal Codex session and `TurnCompleteEvent` did not expose TTFT. The timing needs to flow through the standard turn-completion protocol so Guardian review analytics can consume the same value as the rest of the session machinery. ## What changed Adds optional `time_to_first_token_ms` to `TurnCompleteEvent` and populates it from `TurnTiming`. The value is carried through app-server thread history, rollout reconstruction, TUI/app-server adapters, and Guardian review session handling. Guardian review analytics now captures TTFT from the reviewer turn-complete event when available. Existing tests and fixtures are updated to set the new optional field to `None` where TTFT is not relevant. ## Verification - `cargo clippy -p codex-tui --tests -- -D warnings` - `cargo clippy -p codex-core --lib --tests -- -D warnings` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/17696). * __->__ #17696 * #17695 * #17693 * #18278 * #18953
rhan-oai ·
2026-04-22 01:52:48 -07:00 -
Queue follow-up input during user shell commands (#18820)
Fixes #17954. ## Why When a manual shell command like `!sleep 10` is running, submitting plain text such as `hi` currently sends that text as a steer for the active shell turn. User shell turns are not steerable like model turns, so the TUI can remain stuck in `Working` after the shell command finishes. ## What Changed - Detect when the only active work is one or more `ExecCommandSource::UserShell` commands. - Queue plain submitted input in that state so it drains after the shell command and shell turn complete. - Preserve `!cmd` submissions during running work so explicit shell commands keep their existing behavior. - Add regression coverage for the `!sleep 10` plus `hi` flow in `chatwidget::tests::exec_flow::user_message_during_user_shell_command_is_queued_not_steered`. ## Verification - Manually confirmed hang before the fix and no hang after the fix
Eric Traut ·
2026-04-21 10:13:13 -07:00 -
Queue slash and shell prompts in the TUI (#18542)
## Why Users have asked to queue follow-up slash commands while a task is running, including in #14081, #14588, #14286, and #13779. The previous TUI behavior validated slash commands immediately, so commands that are only meaningful once the current turn is idle could not be queued consistently. The queue should preserve what the user typed and defer command parsing until the item is actually dispatched. This also gives `/fast`, `/review ...`, `/rename ...`, `/model`, `/permissions`, and similar slash workflows the same FIFO behavior as plain queued prompts. ## What Changed - Added a queued-input action enum so queued items can be dispatched as plain prompts, slash commands, or user shell commands. - Changed `Tab` queueing to accept slash-led prompts without validating them up front, then parse and dispatch them when dequeued. - Added `!` shell-command queueing for `Tab` while a task is running, while preserving existing `Enter` behavior for immediate shell execution. - Moved queued slash dispatch through shared slash-command parsing so inline commands, unavailable commands, unknown commands, and local config commands report at dequeue time. - Continued queue draining after local-only actions and after slash menu cancellation or selection when no task is running. - Preserved slash-popup completion behavior so `/mo<Tab>` completes to `/model ` instead of queueing the prefix. - Updated pending-input preview snapshots to show queued follow-up inputs. ## Verification I did a bunch of manual validation (and found and fixed a few bugs along the way).
Eric Traut ·
2026-04-19 10:52:16 -07:00 -
Spread AbsolutePathBuf (#17792)
Mechanical change to promote absolute paths through code.
pakrym-oai ·
2026-04-14 14:26:10 -07:00 -
[codex] Make AbsolutePathBuf joins infallible (#16981)
Having to check for errors every time join is called is painful and unnecessary.
pakrym-oai ·
2026-04-07 10:52:08 -07:00 -
[codex-analytics] add protocol-native turn timestamps (#16638)
--- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16638). * #16870 * #16706 * #16659 * #16641 * #16640 * __->__ #16638
rhan-oai ·
2026-04-06 16:22:59 -07:00 -
Refactor chatwidget tests into topical modules (#16361)
Problem: `chatwidget/tests.rs` had grown into a single oversized test blob that was hard to maintain and exceeded the repo's blob size limit. Solution: split the chatwidget tests into topical modules with a thin root `tests.rs`, shared helper utilities, preserved snapshot naming, and hermetic test config so the refactor stays stable and passes the `codex-tui` test suite.
Eric Traut ·
2026-03-31 16:45:58 -06:00