Skip to content

Comments

尝试合并#1

Merged
zcg merged 49 commits intobyebye-code:mainfrom
joshsymonds:main
Nov 25, 2025
Merged

尝试合并#1
zcg merged 49 commits intobyebye-code:mainfrom
joshsymonds:main

Conversation

@zcg
Copy link

@zcg zcg commented Nov 25, 2025

External (non-OpenAI) Pull Request Requirements

Before opening this Pull Request, please read the dedicated "Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md

If your PR conforms to our contribution guidelines, replace this text with a detailed and high quality description of your changes.

Include a link to a bug report or enhancement request.

Summary by CodeRabbit

  • New Features

    • Added custom status line to the TUI displaying Git branch, Kubernetes context, environment variables, and real-time task status.
    • Introduced workspace-scoped state persistence for model selection and MCP server preferences.
  • Improvements

    • Updated default AI model to gpt-5.1-codex across all platforms.
    • CLI version now includes Git commit SHA for better build traceability.
    • Enhanced TUI layout and rendering for improved visual consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

Walkthrough

This PR introduces a custom status line rendering system for the TUI with Git and Kubernetes context awareness, adds persistent per-workspace state management, updates the default model to gpt-5.1-codex, refactors GitHub Actions workflows, and restructures Nix build configuration. It includes CLI git SHA embedding, file-locking improvements, and extends configuration for TUI customization.

Changes

Cohort / File(s) Summary
GitHub Actions Workflows
.github/workflows/codex-cli-bundles.yml, .github/workflows/rust-ci.yml
Introduces new matrix-based CLI build workflow for linux-x86_64, darwin-x86_64, and darwin-aarch64 with Nix, Cachix caching, and magic-nix-cache integration. Removes comprehensive Rust CI pipeline including format verification, cargo-shear, multi-platform lint/build, tests, and results aggregation.
Workspace Dependencies & Build Scripts
codex-rs/Cargo.toml, codex-rs/cli/Cargo.toml, codex-rs/cli/build.rs
Adds hostname dependency to workspace. Introduces build.rs script for CLI crate to embed git SHA via CODEX_CLI_GIT_SHA environment variable with fallback to git HEAD or "unknown".
CLI Version Integration
codex-rs/cli/src/main.rs
Introduces CLI_VERSION constant concatenating crate version with embedded git SHA. Updates Clap version spec and adds test verifying version string includes commit SHA.
Core Configuration Updates
codex-rs/core/Cargo.toml, codex-rs/core/src/config/mod.rs, codex-rs/core/src/config/types.rs
Updates OPENAI_DEFAULT_MODEL from "gpt-5"/"gpt-5-codex" to "gpt-5.1-codex" for all platforms. Adds tui_custom_statusline boolean field to Config and Tui types with default true. Adds fs2 dependency for file locking; changes Tui derive to remove Default and adds explicit impl.
Workspace State Management
codex-rs/core/src/lib.rs, codex-rs/core/src/workspace_state.rs
Exports new workspace_state module. Implements persistent per-workspace state with deterministic SHA-1 hashed path storage. Provides data models for model selection, reasoning effort, and MCP server enablement with TOML serialization and atomic file writes.
Message History File Locking
codex-rs/core/src/message_history.rs
Replaces advisory file-lock approach with fs2::FileExt-based exclusive/shared locking for writes and reads. Adds explicit unlock calls, improved error propagation, and retry logic with sleep on contention.
Protocol Refactoring
codex-rs/protocol/src/protocol.rs
Refactors conditional block for TMPDIR writable roots from combined condition to nested if guards for improved readability.
TUI Dependencies & Modules
codex-rs/tui/Cargo.toml, codex-rs/tui/src/lib.rs
Adds hostname workspace dependency. Adds statusline module declaration to TUI crate.
App Event & Status Integration
codex-rs/tui/src/app_event.rs, codex-rs/tui/src/app.rs
Introduces StatusLineGit and StatusLineKubeContext variants to AppEvent. Adds event handling in App to update ChatWidget status lines and request frame redraws.
App Backtrack Consistency
codex-rs/tui/src/app_backtrack.rs
Adds status_renderer: None field to ChatWidgetInit for consistency with new overlay support.
Status Line System
codex-rs/tui/src/statusline/mod.rs, codex-rs/tui/src/statusline/palette.rs, codex-rs/tui/src/statusline/skins/mod.rs, codex-rs/tui/src/statusline/state.rs, codex-rs/tui/src/statusline/overlay.rs
Comprehensive new status line rendering system with DefaultStatusLineRenderer, multi-stage degradation strategy, powerline-style segments, Git/Kubernetes context detection, run state tracking, token/context awareness, and environment integration. Includes CustomStatusLineRenderer implementation and async background task management.
ChatWidget Status Line Integration
codex-rs/tui/src/chatwidget.rs, codex-rs/tui/src/chatwidget/tests.rs
Adds status_overlay field to ChatWidget with bootstrap flow. Introduces public methods to update git/kube context and set custom renderers. Wires StatusLineOverlay into render pipeline with BottomPaneWithOverlay bridge. Changes model slug reference for warning text. Tests now create and configure StatusLineOverlay with devspace/hostname mocking for deterministic snapshots.
Bottom Pane & Composer Refactoring
codex-rs/tui/src/bottom_pane/mod.rs, codex-rs/tui/src/bottom_pane/chat_composer.rs, codex-rs/tui/src/bottom_pane/footer.rs
Adds has_active_view and composer_layout_for_tests methods to BottomPane. Introduces ComposerRenderLayout abstraction to ChatComposer with explicit padding/margin constants and WidgetRef-based rendering. Adds desired_height and cursor_pos public methods. Hides ShortcutSummary context indicator and makes context_window_line optional in footer.
Status Module Re-exports
codex-rs/tui/src/status/mod.rs
Exposes line_display_width, truncate_line_to_width, and format_directory_display as crate-public re-exports.
Test Snapshots
codex-rs/tui/src/chatwidget/snapshots/*.snap.new
Adds new snapshot files for reasoning level selection popup, rate limit prompt, and exec/status layout tests.
Build System
flake.nix
Refactors from simple outputs to full inputs (nixpkgs, flake-utils, rust-overlay) and multi-system Rust builds. Adds rustPlatform with nightly toolchain, cargoLock, cargoVendorSha, and commonRustPackageArgs. Introduces codex-cli and codex-tui packages with proper binary metadata and license declarations.
Documentation
customization-plan.md
New documentation detailing TUI customization strategy, layering approach with config flags, patch application workflow, testing strategy, and upstream sync checklist.

Sequence Diagram

sequenceDiagram
    participant App
    participant StatusOverlay as StatusLineOverlay
    participant BGTask as Background Tasks
    participant GitDetect as Git Detection
    participant KubeDetect as Kube Detection
    participant ChatWidget
    participant FrameRequester
    
    App->>StatusOverlay: new() with config flag
    activate StatusOverlay
    StatusOverlay->>StatusOverlay: init with defaults
    
    App->>StatusOverlay: bootstrap(tokens, messages)
    StatusOverlay->>StatusOverlay: sync_model, refresh_environment
    
    StatusOverlay->>BGTask: spawn_background_tasks()
    activate BGTask
    BGTask->>GitDetect: collect_status_line_git_snapshot
    BGTask->>KubeDetect: detect_kube_context_async
    deactivate BGTask
    
    GitDetect-->>App: AppEvent::StatusLineGit(snapshot)
    KubeDetect-->>App: AppEvent::StatusLineKubeContext(context)
    
    App->>ChatWidget: update_statusline_git(snapshot)
    App->>ChatWidget: update_statusline_kube_context(context)
    
    App->>FrameRequester: request_frame()
    FrameRequester->>ChatWidget: render with overlay
    ChatWidget->>StatusOverlay: layout(area)
    ChatWidget->>StatusOverlay: render_status_line(area)
    deactivate StatusOverlay
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Areas requiring extra attention:

  • Status Line Rendering System (codex-rs/tui/src/statusline/) – Extensive new feature with multi-stage rendering degradation, complex layout calculations, Git/Kubernetes context detection, and async background tasks. Multiple interrelated types and rendering paths require careful validation for correctness and edge cases.
  • ChatWidget Integration (codex-rs/tui/src/chatwidget.rs) – Significant refactoring adding StatusLineOverlay field, new public methods, and wiring through render pipeline. State management interactions between overlay, widget, and events need verification.
  • Bottom Pane & Chat Composer Refactoring (codex-rs/tui/src/bottom_pane/chat_composer.rs) – Substantial reworking of layout and rendering logic with introduction of ComposerRenderLayout abstraction. New padding/margin handling and WidgetRef-based rendering require verification against snapshot tests.
  • File Locking Changes (codex-rs/core/src/message_history.rs) – Transition from advisory locks to fs2-based exclusive/shared locking with explicit unlocks. Error handling paths and lock contention retry logic need careful review for deadlock prevention and correctness.
  • Flake.nix Build System – Complete restructuring from simple outputs to formal inputs and multi-system Rust builds with complex toolchain integration. Reproducibility guarantees (cargoLock, cargoVendorSha) and cross-platform build correctness need validation.
  • Test Coverage & Snapshots – New snapshot-based tests for status overlay rendering with mocked devspace/hostname. Verify snapshot stability and that test configuration properly exercises the feature.

🐰 The status line now shimmers bright,
With git and kube insights in sight,
Custom skins paint the UI tale,
Workspace state persists without fail,
Codex CLI bundles take flight!

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description consists entirely of the template placeholder text requesting contributors to read the contributing guidelines and replace the template with a detailed description. No actual description of changes, objectives, or related issues has been provided. Replace the template with a detailed description of the changes, including what was added/modified, why these changes are being made, and a reference to any related issue or feature request.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.47% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title '尝试合并' (Chinese for 'try merging') is vague and does not describe the actual changes in the changeset, which include comprehensive workflow updates, status line implementation, and configuration changes. Replace the title with a descriptive English summary of the main changes, such as 'Add custom status line renderer and refactor CI workflows' or a similarly specific description of the primary objective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello @zcg, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a suite of enhancements aimed at improving the user experience and operational transparency of the Codex TUI and CLI. Key changes include a dynamic and informative status line within the TUI, persistent user preferences for models and MCP servers, and better version traceability for the CLI. These updates collectively make the tool more powerful and user-friendly by providing critical context and maintaining user settings seamlessly.

Highlights

  • Custom TUI Status Line: Introduced a new custom status line in the Terminal User Interface (TUI) that displays dynamic information such as Git branch status, Kubernetes context, environment variables, and real-time task progress.
  • Workspace-Scoped State Persistence: Implemented persistence for user preferences like model selection and MCP server settings, ensuring these choices are remembered across different workspaces.
  • CLI Version Traceability: The CLI version now includes the Git commit SHA, enhancing build traceability and making it easier to identify the exact code version in use.
  • Default AI Model Update: The default AI model has been updated to 'gpt-5.1-codex' across all platforms, improving the baseline intelligence for interactions.
  • TUI Layout and Rendering Enhancements: Significant refactoring of the TUI's layout and rendering logic for the chat composer and status areas, leading to improved visual consistency and responsiveness.
Ignored Files
  • Ignored by pattern: .github/workflows/** (2)
    • .github/workflows/codex-cli-bundles.yml
    • .github/workflows/rust-ci.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant new feature: a custom status line in the TUI, which displays useful context like Git branch and Kubernetes context. It also adds workspace-scoped state persistence. The implementation is quite extensive, especially in the tui crate, with a lot of new modules for the status line. The refactoring of the chat composer layout is a good improvement for maintainability. The addition of the Git commit SHA to the CLI version is also a nice touch for traceability.

However, there are a few critical and medium severity issues that need to be addressed. There are unresolved merge conflicts in some test snapshot files. There's also a potential regression in error handling for image pasting, and some inconsistencies in AI model naming that could cause issues.

Comment on lines 1 to 9
---
source: tui/src/chatwidget/tests.rs
<<<<<<< HEAD
=======
assertion_line: 562
>>>>>>> main
expression: "format!(\"{buf:?}\")"
---
Buffer {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This file contains merge conflict markers (<<<<<<< HEAD, =======, >>>>>>> main). Please resolve these conflicts before merging.


const RATE_LIMIT_WARNING_THRESHOLDS: [f64; 3] = [75.0, 90.0, 95.0];
const NUDGE_MODEL_SLUG: &str = "gpt-5.1-codex-mini";
const NUDGE_MODEL_SLUG: &str = "gpt-5-codex-mini";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There seems to be an inconsistency in the model names used. In core/src/config/mod.rs, OPENAI_DEFAULT_MODEL is updated to gpt-5.1-codex. However, in this file, NUDGE_MODEL_SLUG is changed from gpt-5.1-codex-mini to gpt-5-codex-mini. Another change on line 2076 checks for gpt-5-codex instead of gpt-5.1-codex for high reasoning effort warnings.

Could you please verify the correct model names and ensure they are used consistently across the codebase to avoid issues with model nudges and warnings?

Comment on lines +1284 to 1286
if let Ok((path, info)) = paste_image_to_temp_png() {
self.attach_image(path, info.width, info.height, info.encoded_format.label());
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The error handling for paste_image_to_temp_png has been removed. Previously, if pasting an image failed, a warning was logged and an error message was displayed to the user. Now, it fails silently. This could be confusing for users if pasting an image doesn't work and they don't know why. It would be better to restore the error handling.

                match paste_image_to_temp_png() {
                    Ok((path, info)) => {
                        self.attach_image(path, info.width, info.height, info.encoded_format.label());
                    }
                    Err(err) => {
                        tracing::warn!("failed to paste image: {err}");
                        self.add_to_history(history_cell::new_error_event(format!(
                            "Failed to paste image: {err}",
                        )));
                    }
                }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (21)
codex-rs/cli/build.rs (1)

1-38: Robust git SHA resolution with graceful fallback

The build script correctly prefers CODEX_BUILD_GIT_SHA, falls back to git rev-parse HEAD, and degrades to "unknown" without panicking, then exposes CODEX_CLI_GIT_SHA for use in the binary—this is a solid, failure-tolerant setup. As a minor incremental-build improvement, you could also track changes to the env var:

 fn main() {
-    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-changed=build.rs");
+    println!("cargo:rerun-if-env-changed=CODEX_BUILD_GIT_SHA");

so toggling CODEX_BUILD_GIT_SHA alone will retrigger the build script.

flake.nix (3)

17-17: Pin the nightly toolchain to a specific date for reproducibility.

Using nightly.latest means the Rust toolchain version changes over time, which can cause non-reproducible builds or unexpected breakages when the nightly channel updates.

Consider pinning to a specific nightly date:

-        rustToolchain = pkgs.rust-bin.nightly.latest.default;
+        rustToolchain = pkgs.rust-bin.nightly."2024-11-01".default;

35-38: Inconsistent hash formats in outputHashes.

Line 36 uses SRI format (sha256-...) while line 37 uses base32 format. Both work, but using a consistent format improves readability.

         outputHashes = {
           "ratatui-0.29.0" = "sha256-HBvT5c8GsiCxMffNjJGLmHnvG77A6cqEL+1ARurBXho=";
-          "crossterm-0.28.1" = "0vzgpvbri4m4qydkj50ch468az7myy04qh5z2n500p1f4dysv87a";
+          "crossterm-0.28.1" = "sha256-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=";
         };

You can convert the base32 hash to SRI format using nix hash to-sri --type sha256 <base32-hash>.


97-106: Consider clearer variable naming for app wrappers.

The variable codexApp wraps codex-tui, which is confusing since it sounds like the main app. Consider renaming for clarity.

         apps =
           let
             codexCliApp = flake-utils.lib.mkApp { drv = codex-cli; };
-            codexApp = flake-utils.lib.mkApp { drv = codex-tui; };
+            codexTuiApp = flake-utils.lib.mkApp { drv = codex-tui; };
           in {
             codex = codexCliApp;
             codex-cli = codexCliApp;
-            codex-tui = codexApp;
+            codex-tui = codexTuiApp;
             default = codexCliApp;
           };
codex-rs/core/src/message_history.rs (2)

222-262: Consider logging unlock failures in the read path.

The unlock results are ignored with let _ = FileExt::unlock(&file) at lines 231, 244, and 251. While less critical than exclusive lock failures (since shared locks permit concurrent readers), a failed unlock could still prevent future exclusive locks (writes) from succeeding.

Consider logging unlock failures to aid debugging if lock-related issues occur in production.

Apply this pattern to the three unlock sites:

-                            let _ = FileExt::unlock(&file);
+                            if let Err(e) = FileExt::unlock(&file) {
+                                tracing::warn!(error = %e, "failed to unlock history file after read error");
+                            }

223-252: Consider optimizing lookup for large offsets.

The current implementation reads and parses every line from the start up to the target offset. For large history files, this could be inefficient when looking up recent entries.

If performance becomes an issue, consider:

  1. Maintaining an index file with byte offsets
  2. Using a more sophisticated data structure
  3. Or accepting the current O(n) behavior if history files remain small
codex-rs/core/src/config/mod.rs (1)

61-65: Be aware of tests gated on OPENAI_DEFAULT_MODEL

Bumping OPENAI_DEFAULT_MODEL to "gpt-5.1-codex" will cause tests like binary_size_transcript_snapshot (which early‑returns unless the default is "gpt-5-codex") to be skipped. That looks intentional per the test comment, but worth confirming that you no longer expect that snapshot to run under the new default.

codex-rs/tui/src/statusline/state.rs (1)

27-288: StatusLineState and RunTimer logic look correct, with a couple of small polish opportunities

The state machine around start_task/complete_task, set_idle_run_state, and snapshot_for_render is coherent and integrates cleanly with FrameRequester and RunTimer. Timer pause/resume semantics and queued‑message / interrupt‑hint propagation into StatusLineRunState are sound.

Two minor polish ideas you could consider later:

  • set_session_id is currently a no‑op; either wire it into the snapshot (e.g., for future display) or document it as intentionally unused to avoid confusion.
  • If TokenUsageInfo.model_context_window can be more accurate than the config hint over time, you might want to cache it into self.context_window_hint when present, so subsequent updates without a window still use the most recent value.

Neither is blocking; the current behavior is consistent.

Also applies to: 290-340

codex-rs/tui/src/chatwidget/tests.rs (2)

2828-2988: Status overlay layout tests are valuable; consider tightening assumptions about popup height

The two new tests around cursor alignment and composer padding/margins under an active status overlay are quite thorough and should prevent a lot of subtle regressions in layout and styling.

One small robustness improvement for typed_input_preserves_padding_and_margin_with_status_overlay:

  • The test assumes there is at least one padding row and one margin row (margin_y = popup_rect.y + 1), but only asserts that popup_rect.height >= 1. If composer_layout_for_tests ever returns a popup_rect that’s only a single row tall, indexing at margin_y will end up outside that rectangle (and potentially out of bounds if the pane is tight).

You could either:

  • assert popup_rect.height >= 2, or
  • compute the margin row from popup_rect and/or pane_area more defensively (e.g., let margin_y = (popup_rect.y + 1).min(area.y + area.height - 1)).

Not urgent, but it would make the test more future‑proof against layout refactors.


2994-3077: VT100 snapshot test nicely exercises statusline environment, but uses a global devspace override

chatwidget_exec_and_status_layout_vt100_snapshot now seeds the status overlay with deterministic devspace/hostname/AWS/kube context, which is great for stabilizing the snapshot.

Minor consideration: set_devspace_override_for_tests and clear_devspace_override_for_tests rely on a global, mutex‑protected override. Because Rust tests can run in parallel, there’s a theoretical window between set_* and clear_* where a different test could observe the override. The current usage is very short‑lived and probably fine, but if flakiness ever shows up you may want to:

  • gate this test with -- --test-threads=1 (or a serialization helper), or
  • move the override entirely behind StatusLineOverlay::new parameters and avoid the global for this path.

Functionally, the test is well structured.

codex-rs/tui/src/chatwidget.rs (5)

229-240: Status overlay wiring in ChatWidget looks coherent; consider documenting invariants

The addition of status_renderer in ChatWidgetInit, the status_overlay: Option<StatusLineOverlay> field, and the helper methods (update_statusline_git, update_statusline_kube_context, set_status_renderer, status_line_mut) are all consistently guarded on status_overlay.is_some() and keep the header in sync via set_status_header. From this file it appears:

  • StatusLineOverlay::new(...) is the only place that can yield None, and every later use treats status_overlay as truly optional.
  • set_status_header is now the single source of truth for both the bottom-pane status indicator and the run header shown by the overlay, which is good for consistency.

It would help future maintainers if you briefly documented the expected invariant, e.g. whether status_overlay is Some whenever config.tui_custom_statusline is enabled and whether it can legitimately flip between Some and None at runtime, since a lot of callers now branch on that option.

Also applies to: 250-299, 341-368, 369-375


377-387: Keep status overlay state aligned with session + token + queue state

The new overlay hooks in:

  • on_session_configured (set session id, sync model, spawn background tasks),
  • set_token_info (propagate token usage to overlay.update_tokens),
  • on_undo_started (mirror interrupt visibility and header), and
  • refresh_queued_user_messages (mirror queued messages into the overlay)

all look internally consistent and side-effect free when status_overlay is None. This should keep the overlay’s session/model/tokens/queue view in step with the bottom pane.

One thing to watch: overlay.spawn_background_tasks() is now called both in on_session_configured and after task completion; if that method isn’t idempotent, you might end up with duplicated background workers over a long session. If it already self-guards, no change is needed; otherwise consider making it idempotent or centralizing its call site.

Also applies to: 538-555, 791-803, 1817-1827


902-950: Status overlay integration for exec/patch/MCP flows is consistent; minor duplication only

The new overlay interactions on command and patch flows:

  • handle_exec_begin_now / handle_mcp_begin_now resume the timer and set a specific run header via exec_status_label / tool_status_label.
  • handle_exec_end_now and handle_patch_apply_end_now reset the run header to "Working" and, when there are no more running commands or a patch has completed, call overlay.refresh_git() so the git snapshot matches the new workspace state.
  • handle_exec_approval_now and handle_apply_patch_approval_now set approval-specific run headers and also emit the new Notification::EditApprovalRequested variant as appropriate.
  • handle_mcp_end_now resets the header but deliberately does not refresh git (reasonable if MCP calls are not expected to touch the repo).

This all lines up: the overlay’s run header is a concise reflection of what the system is currently waiting on, and git context refreshing is only done in places that actually change the tree.

If you see more of these lifecycle hooks proliferate, consider pulling the “set header + maybe refresh git” pattern into a tiny helper (e.g., set_idle_header_and_refresh_git_if_idle()) to avoid subtle drift between handlers, but there’s no immediate correctness issue here.

Also applies to: 952-967, 969-1004, 1012-1097


1099-1179: Constructors correctly bootstrap the overlay; consider avoiding double work on empty queues

Both ChatWidget::new and ChatWidget::new_from_existing now:

  • clone frame_requester so both the widget and BottomPane have handles,
  • build a StatusLineOverlay with config, a cloned FrameRequester, app_event_tx, and the optional status_renderer,
  • initialize status_overlay on the struct, and
  • immediately call overlay.bootstrap(&config, token_info.clone(), queued_messages) followed by refresh_queued_user_messages().

Given that queued_user_messages and token_info are empty at construction time, the bootstrap call is effectively a no‑op today; refresh_queued_user_messages() will also push an empty queue into both the bottom pane and overlay.

That’s fine functionally, but if you ever add heavier work to bootstrap (e.g., initial git scan) you may want to:

  • guard the call behind “any initial state to sync?” or
  • let on_session_configured own heavyweight initialization, with constructors only wiring references.

Also applies to: 1181-1265


2885-2908: BottomPaneWithOverlay nicely isolates layout; double‑check future changes to has_active_view

Wrapping the bottom pane in BottomPaneWithOverlay and exposing it via bottom_pane_renderable() is a clean way to:

  • hide the bottom‑pane status indicator when an overlay is active (overlay is filtered out when bottom_pane.has_active_view() is true),
  • let StatusLineOverlay::layout choose pane_area, run_pill_area, and status_line_area, and
  • keep desired_height consistent by adding StatusLineOverlay::reserved_rows() only when an overlay is actually being rendered and there’s no active view.

The render and cursor logic both respect the computed StatusLineLayout, and the use of Clear before drawing overlay regions avoids visual overlap with previous frames.

The only subtle invariant is that bottom_pane_renderable() uses the value of has_active_view() at the time it’s called to decide whether overlay is Some, while overlay_layout() recomputes has_active_view() for the layout call. As long as BottomPane can’t mutate its active‑view flag during a single render pass (which is true in this architecture), this stays consistent; just keep that in mind if you ever introduce side‑effectful rendering or background UI updates.

Also applies to: 2926-2984

codex-rs/tui/src/bottom_pane/chat_composer.rs (2)

139-153: fill_rect_with_style is straightforward; confirm Buffer::cell_mut semantics match your ratatui version

fill_rect_with_style does the expected thing—fills a rect with spaces and the provided style, with early exit on zero-sized rects. It’s used to:

  • paint the top padding and composer background with user_message_style, and
  • initialize the footer and bottom margin rows with default style before delegating to overlays/footers.

A couple of minor points:

  • This runs on every render over the composer region; the areas are small, so the double loop is fine, but if you later need to optimize, you could switch to iterating over a slice of the buffer if ratatui exposes region iterators.
  • The if let Some(cell) = buf.cell_mut((x, y)) pattern assumes cell_mut returns an Option<&mut Cell>. Some ratatui versions expose cell_mut/get_mut as infallible. Please double‑check this against the ratatui version you’re pinned to and adjust (or gate via a small extension trait) if the signature differs, otherwise this will be a type error.

Also applies to: 1687-1693


717-839: File-search popup: image attachment path is careful about UTF‑8 and token boundaries

The changes in handle_key_event_with_file_popup and the new is_image_path helper add a nice quality-of-life improvement:

  • When a file-search selection ends with .png/.jpg/.jpeg, you now attempt to treat it as an image:
    • verify dimensions via image::image_dimensions,
    • compute token boundaries around the cursor using the same whitespace-based logic as insert_selected_path,
    • remove the current @token slice, and
    • attach an image placeholder plus a trailing space, with a label derived from the extension.
  • If dimension probing fails or the path doesn’t look like an image, you fall back to insert_selected_path and keep the existing behavior.
  • All slicing is guarded via clamp_to_char_boundary, so even with multi-byte tokens the start_idx..end_idx range stays on valid UTF‑8 boundaries.

This is a good reuse of the existing token-boundary approach and should feel natural in the UI. If you see further duplication creep between this and insert_selected_path, you could consider a small shared helper that returns (start_idx, end_idx) for the active token, but it’s not urgent.

Also applies to: 841-845, 948-997

codex-rs/tui/src/statusline/mod.rs (2)

398-419: Consider tightening the attempts limit.

The attempts > 8 guard is conservative since degrade_run_capsule only defines 2 degradation operations. The loop will exit after at most 2 successful degradations anyway, making the high limit unnecessary (though harmless).

         attempts += 1;
-        if attempts > 8 {
+        if attempts > 3 {
             return truncate_line_to_width(Line::from(spans), target_width);
         }

1059-1073: Consider documenting the distinction from truncate_text.

This function uses single-character "…" (U+2026) while truncate_text in text_formatting.rs uses "...". The choice is appropriate for the space-constrained status line, but a brief doc comment explaining the rationale would help maintainers understand why both exist.

+/// Truncates text to a maximum number of graphemes, appending "…" if truncated.
+/// Uses single-character ellipsis for space efficiency in the status line,
+/// unlike `truncate_text` which uses "..." for general text formatting.
 fn truncate_graphemes(text: &str, max_graphemes: usize) -> String {
codex-rs/tui/src/statusline/overlay.rs (2)

108-129: Consider logging errors from background tasks.

The fire-and-forget spawning pattern is appropriate for background refresh, but task failures are silently ignored. Consider adding error logging to aid debugging if git or kube detection fails unexpectedly.

         handle.spawn(async move {
             let snapshot = collect_status_line_git_snapshot(cwd).await;
+            if snapshot.is_none() {
+                tracing::debug!("git status collection returned no data");
+            }
             tx.send(AppEvent::StatusLineGit(snapshot));
         });

353-371: Simplistic YAML parsing may miss edge cases.

The line-by-line parsing for current-context: works for typical kubeconfig files but could fail with quoted values, multi-line strings, or unusual formatting. Consider using a YAML parser like serde_yaml for robustness, or document this limitation.

For typical kubeconfig files, this works. If edge cases become problematic, consider:

// Using serde_yaml for robust parsing
#[derive(Deserialize)]
struct KubeConfigPartial {
    #[serde(rename = "current-context")]
    current_context: Option<String>,
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37d83e0 and 7573812.

⛔ Files ignored due to path filters (37)
  • codex-rs/Cargo.lock is excluded by !**/*.lock
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__backspace_after_pastes.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__empty.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_ctrl_c_interrupt.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_ctrl_c_quit.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_ctrl_c_then_esc_hint.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_esc_hint_backtrack.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_esc_hint_from_overlay.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_hidden_while_typing.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_overlay_then_external_esc_hint.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__footer_mode_shortcut_overlay.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__large.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__multiple_pastes.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__slash_popup_mo.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__small.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_context_running.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__footer__tests__footer_shortcuts_default.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__queued_messages_visible_when_status_hidden_snapshot.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_composer_fill_height_without_bottom_padding.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_composer_include_bottom_padding.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_and_queued_messages_snapshot.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_hidden_when_height_too_small_height_1.snap is excluded by !**/*.snap
  • codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__tests__status_hidden_when_height_too_small_height_2.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chat_small_idle_h3.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chat_small_running_h2.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chat_small_running_h3.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_tall.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__exec_approval_modal_exec.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__rate_limit_switch_prompt_popup.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_band_snapshot.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_widget_active.snap is excluded by !**/*.snap
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__status_widget_and_approval_modal.snap is excluded by !**/*.snap
  • codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__apply_update_block_manual.snap is excluded by !**/*.snap
  • codex-rs/tui/src/statusline/snapshots/codex_tui__statusline__tests__statusline_narrow_40.snap is excluded by !**/*.snap
  • codex-rs/tui/src/statusline/snapshots/codex_tui__statusline__tests__statusline_wide_80.snap is excluded by !**/*.snap
  • flake.lock is excluded by !**/*.lock
📒 Files selected for processing (34)
  • .github/workflows/codex-cli-bundles.yml (1 hunks)
  • .github/workflows/rust-ci.yml (0 hunks)
  • codex-rs/Cargo.toml (1 hunks)
  • codex-rs/cli/Cargo.toml (1 hunks)
  • codex-rs/cli/build.rs (1 hunks)
  • codex-rs/cli/src/main.rs (2 hunks)
  • codex-rs/core/Cargo.toml (1 hunks)
  • codex-rs/core/src/config/mod.rs (7 hunks)
  • codex-rs/core/src/config/types.rs (1 hunks)
  • codex-rs/core/src/lib.rs (1 hunks)
  • codex-rs/core/src/message_history.rs (3 hunks)
  • codex-rs/core/src/workspace_state.rs (1 hunks)
  • codex-rs/protocol/src/protocol.rs (1 hunks)
  • codex-rs/tui/Cargo.toml (1 hunks)
  • codex-rs/tui/src/app.rs (4 hunks)
  • codex-rs/tui/src/app_backtrack.rs (1 hunks)
  • codex-rs/tui/src/app_event.rs (2 hunks)
  • codex-rs/tui/src/bottom_pane/chat_composer.rs (9 hunks)
  • codex-rs/tui/src/bottom_pane/footer.rs (3 hunks)
  • codex-rs/tui/src/bottom_pane/mod.rs (2 hunks)
  • codex-rs/tui/src/chatwidget.rs (32 hunks)
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap.new (1 hunks)
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap.new (1 hunks)
  • codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__rate_limit_switch_prompt_popup.snap.new (1 hunks)
  • codex-rs/tui/src/chatwidget/tests.rs (7 hunks)
  • codex-rs/tui/src/lib.rs (1 hunks)
  • codex-rs/tui/src/status/mod.rs (1 hunks)
  • codex-rs/tui/src/statusline/mod.rs (1 hunks)
  • codex-rs/tui/src/statusline/overlay.rs (1 hunks)
  • codex-rs/tui/src/statusline/palette.rs (1 hunks)
  • codex-rs/tui/src/statusline/skins/mod.rs (1 hunks)
  • codex-rs/tui/src/statusline/state.rs (1 hunks)
  • customization-plan.md (1 hunks)
  • flake.nix (1 hunks)
💤 Files with no reviewable changes (1)
  • .github/workflows/rust-ci.yml
🧰 Additional context used
🧬 Code graph analysis (7)
codex-rs/tui/src/status/mod.rs (2)
codex-rs/tui/src/status/format.rs (2)
  • line_display_width (95-99)
  • truncate_line_to_width (101-147)
codex-rs/tui/src/status/helpers.rs (1)
  • format_directory_display (140-161)
codex-rs/tui/src/statusline/skins/mod.rs (1)
codex-rs/tui/src/statusline/mod.rs (5)
  • text (1005-1010)
  • render (71-71)
  • render (346-348)
  • render_run_pill (73-78)
  • render_run_pill (350-357)
codex-rs/tui/src/statusline/palette.rs (3)
codex-rs/tui/src/render/highlight.rs (1)
  • style (52-57)
codex-rs/protocol/src/protocol.rs (2)
  • default (1076-1087)
  • default (1173-1180)
codex-rs/tui/src/statusline/mod.rs (1)
  • default (210-219)
codex-rs/core/src/config/mod.rs (1)
codex-rs/core/src/config/types.rs (3)
  • default (323-329)
  • default (340-342)
  • default (358-363)
codex-rs/tui/src/bottom_pane/mod.rs (2)
codex-rs/core/tests/common/test_codex.rs (1)
  • test (203-205)
codex-rs/tui/src/custom_terminal.rs (1)
  • area (70-72)
codex-rs/tui/src/chatwidget/tests.rs (1)
codex-rs/tui/src/statusline/overlay.rs (3)
  • clear_devspace_override_for_tests (398-400)
  • set_devspace_override_for_tests (393-395)
  • layout (184-229)
codex-rs/tui/src/statusline/overlay.rs (4)
codex-rs/tui/src/text_formatting.rs (1)
  • truncate_text (79-103)
codex-rs/core/src/git_info.rs (1)
  • collect_git_info (57-109)
codex-rs/tui/src/statusline/mod.rs (5)
  • new (323-330)
  • new (438-459)
  • line (1454-1464)
  • text (1005-1010)
  • default (210-219)
codex-rs/tui/src/statusline/state.rs (1)
  • with_renderer (48-67)
🪛 Gitleaks (8.29.0)
flake.nix

[high] 25-25: Sourcegraph is a code search and navigation engine.

(sourcegraph-access-token)


[high] 30-30: Sourcegraph is a code search and navigation engine.

(sourcegraph-access-token)

🔇 Additional comments (74)
codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__rate_limit_switch_prompt_popup.snap.new (1)

1-6: Snapshot metadata and popup text look consistent and focused

The snapshot cleanly captures just the popup’s user-facing prompt and ties it to the correct test source and expression; nothing here appears brittle or problematic for future maintenance.

codex-rs/protocol/src/protocol.rs (1)

395-400: TMPDIR guard refactor looks correct and preserves behavior

The nested if structure cleanly preserves the original intent: $TMPDIR is added only when exclude_tmpdir_env_var is false, the variable exists, and it is non-empty. No correctness or security regressions are apparent.

codex-rs/cli/Cargo.toml (1)

5-5: Build script wiring for CLI git SHA is correct

build = "build.rs" cleanly hooks the new build script into this crate without altering public interfaces or dependency behavior; no issues found.

codex-rs/cli/src/main.rs (1)

39-44: CLI_VERSION wiring and test coverage look solid

The const CLI_VERSION correctly incorporates the crate version and embedded SHA, is properly referenced via version = CLI_VERSION in the clap attributes, and the version_flag_reports_commit_sha test gives good protection against regressions in how the version string is exposed.

Also applies to: 52-54, 736-741

flake.nix (3)

2-6: LGTM!

The input definitions are standard and appropriate for a Rust project with Nix flakes.


23-32: LGTM!

Source dependencies are properly pinned with specific commits and hashes, ensuring reproducible builds. The static analysis warnings for these lines are false positives—these are standard git commit SHAs, not secrets.


74-74: Verify the homepage URL matches this repository.

The homepage metadata points to github.com/sourcegraph/codex, but this PR is in github.com/zcg/codex. If this is a fork intended to diverge, consider updating the homepage to reflect the correct repository.

Also applies to: 85-85

codex-rs/core/src/message_history.rs (1)

30-30: LGTM!

The fs2::FileExt import is correctly placed and necessary for the new file locking implementation.

codex-rs/core/Cargo.toml (2)

87-87: All imports correctly use the new underscore naming convention.

The verification confirms that all code references have been properly updated to use codex_windows_sandbox instead of codex-windows-sandbox. The only remaining reference to the hyphenated name is in a comment at codex-rs/core/src/sandboxing/mod.rs:144, which does not affect functionality. The dependency name change has been correctly propagated throughout the codebase.


86-86: fs2 version 0.4.3 is correct and current. The latest stable version of the Rust crate fs2 is 0.4.3. The dependency addition is using an up-to-date version.

codex-rs/tui/src/app_backtrack.rs (1)

350-350: LGTM! Clean integration point for status renderer.

The addition of the status_renderer: None field to ChatWidgetInit follows the optional, layered approach described in the PR's customization plan. Initializing to None maintains backward compatibility and keeps the status line feature opt-in.

codex-rs/tui/src/bottom_pane/footer.rs (4)

75-77: Clear documentation of new default-hidden behavior.

The updated comment effectively explains the intentional design decision to hide the context meter by default in ShortcutSummary mode.


82-82: Implements hidden-by-default footer in summary mode.

Returning an empty vector in ShortcutSummary mode effectively hides the footer during normal operation, which aligns with the PR's goal of keeping the UI cleaner.


217-220: Context meter intentionally disabled.

The function signature change to return Option<Line> and the no-op implementation (returning None) clearly indicate the context meter feature has been intentionally disabled. The unused parameter is appropriately prefixed with _.


466-485: Good test coverage for context meter hiding.

The test ensures the context meter text doesn't appear even when context_window_percent is provided, confirming the expected behavior change.

.github/workflows/codex-cli-bundles.yml (5)

1-11: LGTM! Clean workflow structure.

The workflow metadata is well-defined with appropriate trigger conditions and concurrency control to prevent redundant builds.


19-28: Good platform coverage with appropriate runners.

The matrix covers the essential platforms (Linux x86_64, macOS x86_64, and macOS ARM64) with correct runner selections.


38-49: Comprehensive Nix directory caching.

The cache configuration covers all relevant Nix directories across both Unix and macOS platforms, with appropriate fallback keys.


61-65: Build command structure looks correct.

The Nix build command uses appropriate flags (--print-build-logs, --log-format raw, --no-link, --print-out-paths) and targets the correct package path with matrix system substitution.


54-59: Manual verification required: CACHIX_AUTH_TOKEN secret access restricted.

The verification script encountered a permission error (HTTP 403) when attempting to check the repository secrets, preventing automated confirmation of whether CACHIX_AUTH_TOKEN is configured. You need to manually verify that the secret is properly set in the repository settings:

  1. Navigate to repository Settings → Secrets and variables → Actions
  2. Confirm CACHIX_AUTH_TOKEN exists and is properly configured for the joshsymonds Cachix cache
customization-plan.md (1)

1-36: Excellent documentation for maintaining custom TUI changes.

This playbook provides clear guidance on the layering strategy, automation approach, and testing requirements. The "Layer, Don't Fork" philosophy and the concrete workflow checklist will help maintain clean separation from upstream changes.

Key strengths:

  • Clear separation between upstream and custom code
  • Emphasis on config-flag-based feature activation
  • Practical automation guidance with git rerere and patch management
  • Comprehensive testing strategy including snapshot tests
codex-rs/tui/src/statusline/palette.rs (2)

5-37: Well-defined color palette with appropriate linting allowances.

The color constants appear to follow the Catppuccin color scheme (based on names like LAVENDER, MAUVE, ROSEWATER), which provides a cohesive visual design. The clippy::disallowed_methods allowance is appropriate for direct RGB color definitions.

The #[allow(dead_code)] on SURFACE0 suggests it's reserved for future use or conditionally used—this is acceptable for a palette module.


39-43: Queue preview style appropriately styled.

The combination of SUBTEXT0 color with ITALIC | DIM modifiers creates a visually distinct style for queue previews, helping differentiate them from active content.

codex-rs/tui/src/lib.rs (1)

69-69: LGTM! Clean module integration.

The statusline module declaration follows the existing crate structure and enables the new status line rendering system introduced in this PR.

codex-rs/core/src/lib.rs (1)

86-86: Workspace state module verified and approved.

The verification confirms that workspace_state module is properly implemented at codex-rs/core/src/workspace_state.rs with a well-defined public API (WorkspaceState, WorkspaceMcpServerState structs, and load_workspace_state, persist_model_selection, persist_mcp_enabled functions) and existing test coverage.

codex-rs/tui/Cargo.toml (1)

50-50: Hostname dependency is properly imported and actively used.

Verification confirms the hostname crate is correctly imported in codex-rs/tui/src/statusline/overlay.rs (line 17) and invoked at line 294 to fetch the system hostname, which is then displayed in the status line. The integration is complete and functioning as intended.

codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__model_reasoning_selection_popup.snap.new (1)

1-13: New snapshot file needs acceptance.

This .snap.new file represents a pending snapshot that needs to be reviewed and accepted. Run cargo insta review to accept the snapshot or cargo insta accept to accept all pending snapshots.

codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__chatwidget_exec_and_status_layout_vt100_snapshot.snap.new (1)

1-20: New snapshot file needs acceptance.

This .snap.new file captures the new status line overlay with Git, model, and Kubernetes context rendering. Run cargo insta review to accept after verifying the expected output matches the implementation.

codex-rs/tui/src/app_event.rs (2)

11-11: LGTM!

Import properly added for the new StatusLineGitSnapshot type.


45-48: LGTM!

New AppEvent variants for status line updates follow the existing enum patterns with clear documentation comments. Using Option appropriately handles the case when Git/Kubernetes context is unavailable.

codex-rs/tui/src/status/mod.rs (1)

8-10: LGTM!

These re-exports properly expose formatting utilities needed by the status line rendering system while maintaining crate-level visibility.

codex-rs/tui/src/bottom_pane/mod.rs (2)

121-123: LGTM!

Simple predicate method that exposes whether a view is active, enabling the status line overlay to make layout decisions.


264-267: LGTM!

Test helper follows the existing pattern of exposing internal state for testing purposes, properly gated behind #[cfg(test)].

codex-rs/tui/src/app.rs (4)

210-210: LGTM!

Setting status_renderer: None for the fresh session initialization is consistent with the pattern across all ChatWidgetInit constructions in production code.


234-234: LGTM!

Consistent with the other initialization paths.


379-379: LGTM!

Consistent with the other initialization paths for NewSession event handling.


479-486: LGTM!

Event handlers for the new status line events follow the established pattern: update the widget state and schedule a frame redraw. The handling is clean and consistent with similar event handlers in this match block.

codex-rs/core/src/config/types.rs (1)

345-370: Tui custom_statusline defaults are wired consistently

The new custom_statusline field, its serde default (Tui::default_custom_statusline) and the manual Default impl for Tui are consistent and keep behavior well-defined when [tui] is missing or partial. No issues from a config/serde perspective.

codex-rs/tui/src/statusline/skins/mod.rs (1)

1-26: CustomStatusLineRenderer is a clean pass‑through wrapper

The renderer correctly delegates to render_status_line and render_status_run_pill, keeping all layout logic centralized. Zero-sized, Debug + Default are appropriate here.

codex-rs/core/src/config/mod.rs (3)

159-163: New tui_custom_statusline field on Config is well‑placed

Adding tui_custom_statusline: bool alongside tui_notifications on Config cleanly exposes the UI toggle at the core layer. Public visibility and doc comment match how it’s consumed from the TUI.


1165-1174: Config→TUI wiring for tui_custom_statusline is correct

Resolving tui_custom_statusline via cfg.tui.as_ref().map(|t| t.custom_statusline).unwrap_or_else(|| Tui::default().custom_statusline) ensures:

  • explicit [tui].custom_statusline is honored,
  • bare [tui] section gets the serde default (true),
  • missing [tui] falls back to Tui::default() (true).

This is consistent with the new Tui defaults.


2850-3148: Precedence fixture expectations updated correctly for new field

The precedence tests now include tui_custom_statusline: true in the expected Config instances. This keeps the “full struct equality” assertions robust against the new field and matches the defaulting behavior from Tui::default().

codex-rs/tui/src/statusline/state.rs (1)

342-399: Context‑window math and timer snapshot behavior are well‑specified and tested

context_percent_remaining uses a fixed 12k baseline, saturating arithmetic, and clamps to [0,100], which matches the expectations in context_snapshot_matches_status_values. RunTimer::snapshot plus RunTimerSnapshot::elapsed_at are validated by run_timer_snapshot_advances_in_real_seconds, and the use of saturating_duration_since avoids underflow. This all looks correct.

Also applies to: 401-447

codex-rs/core/src/workspace_state.rs (1)

16-113: WorkspaceState persistence is straightforward and resilient

The SHA‑1–based workspace_state_path, graceful NotFound handling, and “warn + default” behavior on TOML parse errors give you robust, non‑disruptive per‑workspace state. Pruning empty MCP entries before write and using a temp‑file + persist pattern avoids stale/partial writes. The public helpers (persist_model_selection, persist_mcp_enabled) have clear semantics and are covered by the test. No issues from a correctness standpoint.

codex-rs/tui/src/chatwidget/tests.rs (3)

4-8: New statusline imports match later usage

Bringing CustomStatusLineRenderer, StatusLineOverlay, StatusLineRenderer, and the style types into scope is consistent with the added helpers and tests; there’s no unused or mismatched import here.

Also applies to: 59-60


264-307: ChatWidget test harness now cleanly wires in StatusLineOverlay

make_chatwidget_with_config and its wrappers (make_chatwidget_manual, make_chatwidget_manual_with_custom_statusline) correctly:

  • share a FrameRequester between the bottom pane and the status overlay,
  • gate the custom renderer on cfg.tui_custom_statusline,
  • bootstrap the overlay once and then refresh queued messages.

This gives tests a single, consistent construction path for both configurations (with and without custom statusline) and keeps the struct literal for ChatWidget up to date (including the new status_overlay and status_renderer init path).


848-873: ESC interrupt test exercises both bottom‑pane and statusline reset

esc_interrupt_resets_status_indicator_and_statusline is a good end‑to‑end check: it asserts the emitted Op::Interrupt, verifies the bottom pane clears its running/indicator state, and also inspects the statusline snapshot to confirm the label returns to "Ready when you are". This should catch regressions in how ESC wiring interacts with the overlay.

codex-rs/tui/src/chatwidget.rs (3)

496-512: Centralize task teardown so the overlay can’t get “stuck” in a running state

The task lifecycle is now split across several handlers:

  • on_task_started sets bottom_pane.set_task_running(true), hides the old status indicator when an overlay is active, and calls overlay.start_task("Working").
  • on_task_complete clears running state, calls overlay.complete_task(), and (re)spawns background tasks.
  • halt_running_task (used for Esc/Ctrl‑C while a task is running) clears bottom‑pane state, cancels running commands, calls overlay.complete_task(), and submits Op::Interrupt.
  • finalize_turn (used from on_error and on_interrupted_turn) clears bottom‑pane state and running commands, but does not touch the overlay.

If codex-core ever ends a turn via Error or TurnAborted without also emitting a TaskComplete, finalize_turn will run but the overlay will never see complete_task(), so its timer/run-pill could remain in a “running” state even though the bottom pane has stopped.

It would be more robust to funnel all “task has ended” flows through a single helper that:

  • resets bottom‑pane flags,
  • clears running_commands and stream_controller, and
  • calls overlay.complete_task() when present.

You could either:

  • move the overlay cleanup into finalize_turn() and have on_task_complete/halt_running_task call that helper, or
  • have finalize_turn() explicitly delegate to a new finish_task_ui() that owns all shared teardown, including the overlay.

[ suggest_recommended_refactor ]

Also applies to: 514-537, 612-621, 623-672, 2618-2644


1267-1300: ESC/Ctrl‑C behavior for running tasks is clearer; ESC short‑circuits appropriately

The changes to input handling:

  • Treat Ctrl‑V as “paste image if possible, otherwise no-op”, which simplifies error handling for failed image pastes.
  • Intercept Esc presses while bottom_pane.is_task_running() and call halt_running_task() instead of letting the composer consume Esc as UI chrome.
  • Have on_ctrl_c delegate to halt_running_task() when a task is running, and only fall back to shutdown when idle.

halt_running_task() centralizes the “user‑initiated interrupt” behavior by:

  • clearing the Ctrl‑C hint and interrupt hint,
  • resetting is_task_running and running_commands,
  • turning off overlay interrupt hints and calling overlay.complete_task(), and
  • sending Op::Interrupt plus a redraw.

This is a good separation between “cancel this turn” and “shut down the app”, and keeps the overlay and bottom pane in sync for user‑driven interrupts.

Also applies to: 2618-2644


1817-1828: Queued messages are now surfaced coherently to both bottom pane and overlay

refresh_queued_user_messages now:

  • rebuilds a Vec<String> of queued texts,
  • passes a clone to bottom_pane.set_queued_user_messages, and
  • forwards the original vector into overlay.set_queued_messages when present.

The small clone is negligible here and ensures both the composer UI and status overlay share an identical view of queued inputs. No issues.

codex-rs/tui/src/bottom_pane/chat_composer.rs (3)

120-137: New layout abstraction (ComposerRenderLayout) is sound and keeps rendering/cursor math consistent

The introduction of:

  • explicit layout constants (TOP_PADDING_HEIGHT, BOTTOM_PADDING_HEIGHT, BOTTOM_MARGIN_HEIGHT, TEXTAREA_RIGHT_MARGIN, FOOTER_SPACING_HEIGHT),
  • the ComposerRenderLayout struct, and
  • shared helpers desired_height, render_layout, layout_areas, and cursor_pos

gives a single source of truth for how the composer occupies its rectangle. In particular:

  • desired_height and render_layout now agree on how many rows go to text vs. footer vs. padding/margin, with text_height clamped to at least 1 and grown to fill any slack in the provided area.height.
  • cursor_pos uses the same layout_areas as rendering, which matches the composer_cursor_aligns_with_text_row test and prevents drift between visual and logical cursor positions.
  • The WidgetRef implementation delegates text rendering strictly to textarea_rect, while the padding/footers are handled via ComposerRenderLayout, so the same plan drives both drawing and hit-testing.

Overall this is a solid refactor; the structure is clear and the constants make future tweaks straightforward.

Also applies to: 193-213, 215-303, 304-316, 326-331, 1681-1759, 1762-1774


1844-2056: Layout-focused tests and snapshot harness give strong coverage of the new rendering plan

The new/updated tests around the composer layout:

  • composer_cursor_aligns_with_text_row validates that cursor_pos lands on the same row as the rendered prompt glyph .
  • composer_renders_bottom_padding_row, composer_leaves_transparent_margin_below_padding, and footer_hint_row_is_separated_from_composer collectively verify:
    • top/bottom padding rows share the same background as the input row,
    • the row(s) used for footer/margin are blank in terms of glyphs, and
    • the margin sits visually below the composer, not merged into it.
  • snapshot_composer_state and the various footer-mode snapshot tests now exercise the WidgetRef path (render_widget_ref) and use footer_height + footer_spacing + BOTTOM_MARGIN_HEIGHT + 8 to size the terminal, giving deterministic snapshots aligned with the new layout.

These tests sharply reduce the risk of subtle regressions in cursor placement or footer spacing; they’re a good complement to the refactored layout code.

Also applies to: 2079-2083, 2601-2603, 2628-2634


2490-2522: Image and large-paste placeholder handling remains robust with new layout

The existing behavior for:

  • large pastes (using [Pasted Content N chars] placeholders),
  • image attachments (with [file.png WxH] placeholders),
  • deleting placeholders (including duplicates and multi-byte text before/after), and
  • flushing paste bursts into either real text or placeholders

is all still well-covered by tests like:

  • handle_paste_large_uses_placeholder_and_replaces_on_submit,
  • test_multiple_pastes_submission,
  • test_placeholder_deletion / test_partial_placeholder_deletion,
  • and the image-specific tests at the bottom of the file.

Given the layout refactor didn’t need to special-case these behaviors in rendering, it’s good to see this test surface untouched but still passing through the new WidgetRef path. No issues here.

Also applies to: 2950-3025, 3077-3112, 3149-3198, 3200-3229

codex-rs/tui/src/statusline/mod.rs (13)

1-48: LGTM! Well-organized module structure and imports.

The module structure is clean with clear separation between public re-exports and internal palette imports. The sub-module organization (overlay, palette, skins, state) provides good encapsulation.


50-69: LGTM! Clear constant definitions for visual elements.

The Powerline glyphs, progress bar characters, and icon arrays are well-defined. The hash-based icon selection approach provides visual variety without requiring persistent mapping.


70-115: LGTM! Clean trait design with useful helper functions.

The StatusLineRenderer trait with Send + Sync bounds enables flexible renderer injection while maintaining thread safety. The helper functions provide a concise DSL for building styled spans.


117-240: LGTM! Well-structured snapshot types with safe arithmetic.

The snapshot hierarchy is clean with proper separation of concerns. Using saturating_add and saturating_duration_since in RunTimerSnapshot::elapsed_at correctly handles edge cases without panicking. The #[allow(dead_code)] annotations suggest planned future use for the unused fields.


242-255: LGTM! Clear duration formatting logic.

The three-tier formatting (seconds, minutes+seconds, hours+minutes+seconds) provides appropriate granularity for different durations with consistent zero-padding.


257-340: LGTM! Well-designed enums for progressive degradation.

The variant enums and DegradeOp provide a clear vocabulary for the degradation system. EnvironmentInclusion with its factory methods cleanly tracks which environment segments to include.


437-459: LGTM! Sensible default rendering configuration.

The initial render model configuration prioritizes essential information (path, model, context bar, git status) while keeping tokens hidden until needed. The environment inclusion is correctly derived from the snapshot data availability.


601-624: LGTM! Robust width calculation with proper overflow handling.

Using checked_sub prevents underflow, and the exact-width requirement integrates well with the degradation loop. The debug_assert provides development-time validation without release overhead.


626-897: LGTM! Well-structured Powerline segment rendering.

The left/right segment rendering maintains visual symmetry with appropriate chevron bridging. The environment segments in collect_right_segments are ordered logically, and truncation is applied consistently to prevent overflow.


949-986: LGTM! Thoughtful context bar implementation with graceful degradation.

The multiple width checks ensure the bar degrades gracefully at small sizes. The color thresholds (green ≤60%, yellow ≤80%, peach ≤92%, red >92%) provide intuitive visual feedback for context usage.


1144-1166: LGTM! Clean token count formatting with suffix optimization.

The logic correctly handles negative values and provides human-readable suffixes. The ".0" suffix removal keeps the display compact.


1206-1485: LGTM! Comprehensive test coverage with snapshot testing.

The test suite covers formatting edge cases, degradation behavior, and renderer parity. The sample_snapshot() fixture provides rich test data, and snapshot tests will catch visual regressions effectively.


474-476: The project uses Rust 1.90.0 (specified in rust-toolchain.toml), which is well beyond the 1.76.0 requirement for let chains. The code is compatible with the project's Rust toolchain and no changes are needed.

codex-rs/tui/src/statusline/overlay.rs (8)

1-41: LGTM! Clean module structure and type definitions.

The imports are well-organized, and StatusLineLayout provides a clear structure for layout regions. The StatusLineOverlay struct appropriately encapsulates state, event sender, and working directory.


43-72: LGTM! Clean feature-gated construction with sensible defaults.

The layout constants are well-documented, and RESERVED_ROWS being a compile-time constant is efficient. The feature flag check (tui_custom_statusline) provides clean opt-in behavior.


74-97: LGTM! Well-structured initialization and synchronization.

The bootstrap method provides a clear initialization sequence, and refresh_environment correctly handles synchronous environment variable detection.


184-229: LGTM! Robust layout calculation with proper bounds checking.

The minimum height check prevents rendering in cramped spaces, and saturating arithmetic ensures no overflow. The status line anchoring to the bottom row is correctly calculated.


241-262: LGTM! Clean status label generation with consistent truncation.

The label generators handle edge cases (empty commands, empty server names) and apply consistent 40-character truncation for display uniformity.


264-295: LGTM! Comprehensive environment detection with sensible fallbacks.

The AWS profile detection correctly handles both AWS_PROFILE and AWS_VAULT, and the hostname detection provides system fallback when the environment variable is unset. The test override mechanism for devspace enables controlled testing.


373-385: LGTM! Proper kubeconfig path resolution with sensible trimming.

The KUBECONFIG environment variable handling correctly supports multiple colon-separated paths. The trim_kube_context function appropriately extracts the cluster name from ARN-style contexts.


402-491: LGTM! Thorough test coverage for layout and rendering.

The overlay_for_tests helper provides clean test setup, and the tests verify critical layout invariants (margin positioning, blank cells). The comprehensive cell style assertions in render_leaves_blank_margin_row ensure margin transparency.

eventsource-stream = "0.2.3"
futures = { version = "0.3", default-features = false }
http = "1.3.1"
hostname = "0.4.0"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

LGTM!

The hostname crate addition is properly placed in alphabetical order and enables the hostname detection feature for the status line rendering system.


🌐 Web query:

What is the latest version of the hostname Rust crate?

💡 Result:

The latest version is hostname 0.4.1 (released April 8, 2025). [1][2]

Sources:
[1] lib.rs — hostname crate (release list).
[2] docs.rs — hostname 0.4.1 documentation.


Update hostname dependency to the latest patch version (0.4.1)

The hostname crate has been updated to 0.4.1 as of April 8, 2025. Update the Cargo.toml entry from hostname = "0.4.0" to hostname = "0.4.1" to incorporate any bug fixes or improvements in the newer patch release.

🤖 Prompt for AI Agents
In codex-rs/Cargo.toml at line 124, the hostname dependency is pinned to
"0.4.0"; update that line to "hostname = \"0.4.1\"" to use the latest patch
release, then run cargo update -p hostname (or cargo build) to refresh the
lockfile and verify the project compiles.

Comment on lines +118 to 132
match FileExt::try_lock_exclusive(&history_file) {
Ok(()) => {
// While holding the exclusive lock, write the full line.
history_file.write_all(line.as_bytes())?;
history_file.flush()?;
let write_result = history_file
.write_all(line.as_bytes())
.and_then(|_| history_file.flush());
let unlock_result = FileExt::unlock(&history_file);
write_result?;
unlock_result?;
return Ok(());
}
Err(std::fs::TryLockError::WouldBlock) => {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
std::thread::sleep(RETRY_SLEEP);
}
Err(e) => return Err(e.into()),
Err(err) => return Err(err),
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle unlock failure more carefully after successful write.

If the write succeeds but unlock fails (line 125), the function returns an error while leaving the file locked exclusively. This could block all subsequent operations on the history file until the process exits.

Consider:

  1. Logging the unlock failure but returning success since the write completed
  2. Or attempting to recover by retrying the unlock
  3. At minimum, add a warning log before returning the unlock error so the failure mode is observable

Apply this diff to log unlock failures while preserving write success:

                 Ok(()) => {
                     let write_result = history_file
                         .write_all(line.as_bytes())
                         .and_then(|_| history_file.flush());
-                    let unlock_result = FileExt::unlock(&history_file);
                     write_result?;
-                    unlock_result?;
+                    if let Err(e) = FileExt::unlock(&history_file) {
+                        tracing::warn!(error = %e, "failed to unlock history file after write; lock may persist until process exit");
+                    }
                     return Ok(());
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
match FileExt::try_lock_exclusive(&history_file) {
Ok(()) => {
// While holding the exclusive lock, write the full line.
history_file.write_all(line.as_bytes())?;
history_file.flush()?;
let write_result = history_file
.write_all(line.as_bytes())
.and_then(|_| history_file.flush());
let unlock_result = FileExt::unlock(&history_file);
write_result?;
unlock_result?;
return Ok(());
}
Err(std::fs::TryLockError::WouldBlock) => {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
std::thread::sleep(RETRY_SLEEP);
}
Err(e) => return Err(e.into()),
Err(err) => return Err(err),
}
match FileExt::try_lock_exclusive(&history_file) {
Ok(()) => {
let write_result = history_file
.write_all(line.as_bytes())
.and_then(|_| history_file.flush());
write_result?;
if let Err(e) = FileExt::unlock(&history_file) {
tracing::warn!(error = %e, "failed to unlock history file after write; lock may persist until process exit");
}
return Ok(());
}
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
std::thread::sleep(RETRY_SLEEP);
}
Err(err) => return Err(err),
}

Comment on lines +310 to +344
async fn git_status_porcelain(cwd: &Path) -> Option<(bool, Option<i64>, Option<i64>)> {
let output = Command::new("git")
.args(["status", "--porcelain=2", "--branch"])
.current_dir(cwd)
.output()
.await
.ok()?;
if !output.status.success() {
return None;
}
let text = String::from_utf8_lossy(&output.stdout);
let mut dirty = false;
let mut ahead = None;
let mut behind = None;
for line in text.lines() {
if !line.starts_with('#') {
dirty = true;
continue;
}
if let Some(rest) = line.strip_prefix("# branch.ab ") {
let mut parts = rest.split_whitespace();
if let Some(ahead_part) = parts.next() {
ahead = ahead_part
.strip_prefix('+')
.and_then(|s| s.parse::<i64>().ok());
}
if let Some(behind_part) = parts.next() {
behind = behind_part
.strip_prefix('-')
.and_then(|s| s.parse::<i64>().ok());
}
}
}
Some((dirty, ahead, behind))
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add timeout to prevent indefinite hanging.

The git status command runs without a timeout. If git hangs (e.g., on network filesystems, large repos, or lock contention), this could block the async runtime indefinitely. The codebase already has run_git_command_with_timeout in codex_core::git_info.rs that could be adapted, or use tokio::time::timeout.

+use tokio::time::timeout;
+use std::time::Duration;
+
+const GIT_COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
+
 async fn git_status_porcelain(cwd: &Path) -> Option<(bool, Option<i64>, Option<i64>)> {
-    let output = Command::new("git")
+    let output = timeout(
+        GIT_COMMAND_TIMEOUT,
+        Command::new("git")
         .args(["status", "--porcelain=2", "--branch"])
         .current_dir(cwd)
         .output()
-        .await
-        .ok()?;
+    )
+    .await
+    .ok()?
+    .ok()?;
     if !output.status.success() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async fn git_status_porcelain(cwd: &Path) -> Option<(bool, Option<i64>, Option<i64>)> {
let output = Command::new("git")
.args(["status", "--porcelain=2", "--branch"])
.current_dir(cwd)
.output()
.await
.ok()?;
if !output.status.success() {
return None;
}
let text = String::from_utf8_lossy(&output.stdout);
let mut dirty = false;
let mut ahead = None;
let mut behind = None;
for line in text.lines() {
if !line.starts_with('#') {
dirty = true;
continue;
}
if let Some(rest) = line.strip_prefix("# branch.ab ") {
let mut parts = rest.split_whitespace();
if let Some(ahead_part) = parts.next() {
ahead = ahead_part
.strip_prefix('+')
.and_then(|s| s.parse::<i64>().ok());
}
if let Some(behind_part) = parts.next() {
behind = behind_part
.strip_prefix('-')
.and_then(|s| s.parse::<i64>().ok());
}
}
}
Some((dirty, ahead, behind))
}
use tokio::time::timeout;
use std::time::Duration;
const GIT_COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
async fn git_status_porcelain(cwd: &Path) -> Option<(bool, Option<i64>, Option<i64>)> {
let output = timeout(
GIT_COMMAND_TIMEOUT,
Command::new("git")
.args(["status", "--porcelain=2", "--branch"])
.current_dir(cwd)
.output()
)
.await
.ok()?
.ok()?;
if !output.status.success() {
return None;
}
let text = String::from_utf8_lossy(&output.stdout);
let mut dirty = false;
let mut ahead = None;
let mut behind = None;
for line in text.lines() {
if !line.starts_with('#') {
dirty = true;
continue;
}
if let Some(rest) = line.strip_prefix("# branch.ab ") {
let mut parts = rest.split_whitespace();
if let Some(ahead_part) = parts.next() {
ahead = ahead_part
.strip_prefix('+')
.and_then(|s| s.parse::<i64>().ok());
}
if let Some(behind_part) = parts.next() {
behind = behind_part
.strip_prefix('-')
.and_then(|s| s.parse::<i64>().ok());
}
}
}
Some((dirty, ahead, behind))
}

Comment on lines +52 to +53
inherit cargoLock;
cargoSha256 = cargoVendorSha;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify if both cargoLock and cargoSha256 are needed.

When using cargoLock with outputHashes, cargoSha256 is typically unnecessary as the lock file handles dependency pinning. Setting both may be redundant or could cause conflicts depending on the nixpkgs version.


🌐 Web query:

Nix buildRustPackage cargoLock vs cargoSha256

💡 Result:

Short answer

  • cargoSha256 (aka cargoHash/cargoHash SRI) — a single hash of the vendored crate tarball. Use it when all dependencies are crates.io (no git/path deps) and you let buildRustPackage fetch+vendor them. Typical workflow: set cargoSha256 = lib.fakeSha256, build, copy the failure hash. [1][2]

  • cargoLock — provide a Cargo.lock (or its contents) so Nix vendors exactly what's listed and you supply per-crate output hashes in cargoLock.outputHashes. Use this when you have git/path dependencies or need to pin the exact lockfile (e.g. local or git deps), or when you want to avoid re-calculating one global cargoSha256 after every lockfile change. You must list each crate’s hash in outputHashes (or let the build fail with fake hashes and copy them). [1][3]

When to pick which

  • Prefer cargoSha256 for simple projects with only crates.io crates (easier: single hash). [1][2]
  • Use cargoLock when the lockfile contains git/path deps or you want deterministic vendoring tied exactly to Cargo.lock — required for git deps. Also handy in flakes or when committing Cargo.lock into repo. [1][3]

Practical tips

  • To discover correct cargoSha256/cargoHash: use lib.fakeSha256 / lib.fakeHash, run the build, and copy the hash from the error. [1]
  • For cargoLock with many git deps, you’ll need to populate cargoLock.outputHashes; you can start with fake hashes and copy actual ones from the failed build output. Tools/docs show examples. [3]
  • cargoDepsName can make the vendored tarball name stable across versions (so the cargo hash is invariant). [1]

Sources

  • Nixpkgs rust docs (buildRustPackage, cargoSha256, cargoLock). [1]
  • Nixpkgs user guide / examples mentioning cargoSha256 and workflow. [2]
  • Blog / how-to showing cargoLock with git deps and outputHashes example. [3]

I need more specific information about whether using both cargoLock and cargoSha256 together actually causes issues.

Based on the official Nixpkgs documentation, the original review comment's concern is valid and well-founded.

The cargoLock argument to buildRustPackage can be used "in place of" cargo{Sha256,Hash}—not alongside it. When using cargoLock, it "does not require a Cargo hash" and retrieves all dependencies as fixed-output derivations. The official Nix documentation demonstrates this with a concrete example: the PR migrating bitwarden_rs explicitly commented out the cargoSha256 line when adding cargoLock, showing they are mutually exclusive approaches, not complementary settings.


Remove cargoSha256 = cargoVendorSha; and rely solely on cargoLock with proper outputHashes.

When using cargoLock, the per-crate hashes in outputHashes handle dependency pinning—cargoSha256 becomes redundant and should be omitted entirely. The lock file provides the determinism that cargoSha256 would otherwise provide.

🤖 Prompt for AI Agents
In flake.nix around lines 52-53, remove the mutually-exclusive cargoSha256
assignment: drop "cargoSha256 = cargoVendorSha;" and rely solely on the provided
cargoLock and its outputHashes for dependency pinning; ensure outputHashes
contains the required per-crate hashes (or update outputHashes as needed) so
buildRustPackage uses cargoLock without specifying cargoSha256.

@zcg zcg merged commit 9107fb2 into byebye-code:main Nov 25, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants