Skip to content

feat(codex): persist Desktop Git settings - #2085

Merged
shunkakinoki merged 1 commit into
mainfrom
codex/persist-codex-desktop-settings
Jul 16, 2026
Merged

feat(codex): persist Desktop Git settings#2085
shunkakinoki merged 1 commit into
mainfrom
codex/persist-codex-desktop-settings

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Summary

  • persist Codex Desktop Git preferences through the dotfiles activation
  • enforce squash merges and keep 300 worktrees with automatic cleanup enabled
  • keep inline review delivery in managed config.toml and preserve unrelated Desktop state

Validation

  • shellcheck config/codex/activate.sh
  • shellspec spec/activate_config_spec.sh (48 examples)
  • make shell-test (1,630 ShellSpec examples and 393 Fish tests)
  • make nix-format-check
  • make nix-test
  • make build
  • live Desktop state verified as squash and 300

Summary by cubic

Persist Codex Desktop Git and worktree settings across Home Manager activations by merging managed keys into the Desktop global state. Enforces squash merges and keeps 300 worktrees, while preserving unrelated Desktop state.

  • New Features
    • Merge managed Git/worktree prefs into .codex-global-state.json using jq (no overwrite of unrelated state).
    • Enforce git-pull-request-merge-method: "squash", worktree-auto-cleanup-enabled: true, and worktree-keep-count: 300.
    • Set reviewDelivery = "inline" in both config.toml and template.
    • Updated activation to accept desktop-settings.json and jq; added tests to verify merge and persistence.

Written for commit d0c0f39. Summary will update on new commits.

Review in cubic

Keep Codex Desktop Git and worktree preferences declarative across Home Manager activations. Preserve unrelated Desktop state while enforcing squash merges and retaining 300 worktrees.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
@cursor

cursor Bot commented Jul 16, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd4bf6e5-aed3-4ea4-8f7c-918629bada73

📥 Commits

Reviewing files that changed from the base of the PR and between f092df0 and d0c0f39.

📒 Files selected for processing (6)
  • config/codex/activate.sh
  • config/codex/config.toml
  • config/codex/config.tpl.toml
  • config/codex/default.nix
  • config/codex/desktop-settings.json
  • spec/activate_config_spec.sh

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added Codex Desktop preferences for inline review delivery, Git branch naming, draft pull requests, squash merges, automatic worktree cleanup, and worktree retention.
    • Preserves existing Codex Desktop settings while updating managed preferences during activation.
  • Bug Fixes

    • Improved activation handling to safely merge and persist Codex Desktop configuration without overwriting unrelated settings.
  • Tests

    • Added coverage for preference persistence, configuration updates, and preservation of existing global settings.

Walkthrough

Codex activation now accepts Desktop settings and jq, installs Desktop Git/worktree preferences, enables inline review delivery, and merges persisted Desktop state into the global state file while preserving unrelated keys.

Changes

Codex Desktop state integration

Layer / File(s) Summary
Desktop configuration and activation inputs
config/codex/desktop-settings.json, config/codex/config.toml, config/codex/config.tpl.toml, config/codex/default.nix
Defines Git/worktree preferences, adds inline review delivery settings, and passes Desktop settings plus jq into activation.
Persisted Desktop state merge
config/codex/activate.sh, spec/activate_config_spec.sh
Merges Desktop-managed state atomically into Codex global state and tests preference values, inline delivery, and preservation of unrelated state.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant A as HomeActivation
  participant B as activate.sh
  participant C as jq
  participant D as CodexGlobalState
  A->>B: pass Codex settings and jq
  B->>C: merge persisted Desktop state
  C->>D: write temporary merged state
  B->>D: atomically replace global state
Loading

Possibly related PRs

Suggested labels: enhancement

Poem

A rabbit hops through Codex’s door,
With inline reviews and settings galore.
State merges cleanly, keys stay in place,
Desktop wishes join the file with grace.
jq helps the burrow bloom!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/persist-codex-desktop-settings

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.

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

Copy link
Copy Markdown
Contributor

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 updates the Codex configuration activation script to support merging managed Desktop settings into the global state store using jq. It also adds a new configuration file for desktop settings, updates the Nix activation logic, and includes corresponding tests to verify the merging behavior and configuration updates. I have provided a suggestion to simplify the jq logic in the activation script by using the inputs filter to handle both existing and new state files more concisely.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread config/codex/activate.sh
Comment on lines +23 to +32
if [[ -s $GLOBAL_STATE ]]; then
"$JQ_BIN" --slurpfile settings "$DESKTOP_SETTINGS_JSON" '
.["electron-persisted-atom-state"] =
((.["electron-persisted-atom-state"] // {}) + $settings[0])
' "$GLOBAL_STATE" >"$GLOBAL_STATE_TMP"
else
"$JQ_BIN" -n --slurpfile settings "$DESKTOP_SETTINGS_JSON" '
{"electron-persisted-atom-state": $settings[0]}
' >"$GLOBAL_STATE_TMP"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The if/else block for handling an existing or new global state file can be simplified. By ensuring the file exists (e.g., with touch) and then using jq's inputs filter with a fallback, you can use a single jq command to handle both cases where the file is new/empty or already contains state. This removes code duplication and makes the logic more concise.

Suggested change
if [[ -s $GLOBAL_STATE ]]; then
"$JQ_BIN" --slurpfile settings "$DESKTOP_SETTINGS_JSON" '
.["electron-persisted-atom-state"] =
((.["electron-persisted-atom-state"] // {}) + $settings[0])
' "$GLOBAL_STATE" >"$GLOBAL_STATE_TMP"
else
"$JQ_BIN" -n --slurpfile settings "$DESKTOP_SETTINGS_JSON" '
{"electron-persisted-atom-state": $settings[0]}
' >"$GLOBAL_STATE_TMP"
fi
touch "$GLOBAL_STATE"
"$JQ_BIN" -n --slurpfile settings "$DESKTOP_SETTINGS_JSON" '
(inputs // {}) |
.["electron-persisted-atom-state"] =
((.["electron-persisted-atom-state"] // {}) + $settings[0])
' "$GLOBAL_STATE" >"$GLOBAL_STATE_TMP"

@mesa-dot-dev

mesa-dot-dev Bot commented Jul 16, 2026

Copy link
Copy Markdown

Mesa Description

TL;DR

Persist Codex Desktop Git and worktree settings across dotfiles activation by merging managed configuration keys into the Desktop global state while preserving unrelated user preferences.

What changed?

  • config/codex/desktop-settings.json: Added a new configuration file defining Git and worktree automation preferences, including forcing squash merges and keeping 300 worktrees with auto-cleanup enabled.
  • config/codex/activate.sh: Updated the activation script to merge the managed settings into Codex's global state JSON file ($HOME/.codex/.codex-global-state.json) using jq, and secured the file permissions to 600.
  • config/codex/default.nix: Updated the activation script call to pass the desktop-settings.json path and the jq binary path as new arguments.
  • config/codex/config.toml & config/codex/config.tpl.toml: Added the reviewDelivery configuration option set to "inline".
  • spec/activate_config_spec.sh: Added integration tests to verify Codex Desktop configuration merging, preference declarations, and ensure existing user configurations are not overwritten.

Description generated by Mesa. Update settings

@shunkakinoki
shunkakinoki marked this pull request as ready for review July 16, 2026 22:12
@indent-zero

indent-zero Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor
PR Summary

Makes Codex Desktop's Git and worktree preferences declarative so they're re-applied on every Home Manager activation without disturbing unrelated Desktop state. Adds a managed-keys JSON file, a jq-based merge step in the Codex activation script, and matching spec coverage; also persists reviewDelivery = "inline" in both config.toml and its template.

  • config/codex/desktop-settings.json (new): declares the six managed keys (git-branch-prefix=codex/, git-always-force-push, git-create-pull-request-as-draft, git-pull-request-merge-method=squash, worktree-auto-cleanup-enabled, worktree-keep-count=300).
  • config/codex/activate.sh: new signature <config_toml> <hooks_json> <desktop_settings_json> <jq_bin>; merges managed keys into ~/.codex/.codex-global-state.json under electron-persisted-atom-state using jq object + (shallow, right-precedence), via mktemp + mv -f for atomic rename, with an EXIT trap that cleans up the temp on failure. Handles missing / zero-byte / populated existing state.
  • config/codex/default.nix: passes the two new args and pins ${pkgs.jq}/bin/jq.
  • config/codex/config.toml and config/codex/config.tpl.toml: both add reviewDelivery = "inline" under [desktop] (template kept in sync so scripts/llm-update.sh regen doesn't drop it).
  • spec/activate_config_spec.sh: adds assertions on the managed keys, the new TOML key, and an end-to-end merge test that seeds unrelated top-level + opposing values and verifies preservation vs. override.

Issues

1 potential issue found:

  • Trailing trap - EXIT in config/codex/activate.sh is unnecessary — after mv -f "$GLOBAL_STATE_TMP" "$GLOBAL_STATE" the temp file no longer exists, so the trap's rm -f is already a no-op and the script is about to exit anyway. → Autofix

CI Checks

Waiting for CI checks...


⚡ Autofix All Issues

@shunkakinoki
shunkakinoki merged commit 4e2b2f8 into main Jul 16, 2026
34 of 37 checks passed
@shunkakinoki
shunkakinoki deleted the codex/persist-codex-desktop-settings branch July 16, 2026 22:12

@mesa-dot-dev mesa-dot-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Performed full review of f092df0...d0c0f39

Analysis

No significant architectural issues identified. The PR demonstrates solid engineering practices: atomic writes via temp file + mv pattern, proper state management with jq shallow merge semantics, clear separation between desktop app state and CLI config, and defensive coding patterns (permission hardening, trap cleanup). The fail-fast error handling is appropriate for configuration management. All dependencies are properly declared and the addition to the activation pipeline is backward-compatible. Test coverage validates critical behaviors including state preservation and managed key overrides.

Tip

Help

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

6 files reviewed | 3 comments | Edit Agent SettingsRead Docs

Comment thread config/codex/activate.sh
if [[ -s $GLOBAL_STATE ]]; then
"$JQ_BIN" --slurpfile settings "$DESKTOP_SETTINGS_JSON" '
.["electron-persisted-atom-state"] =
((.["electron-persisted-atom-state"] // {}) + $settings[0])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

High

The shallow merge using + operator will overwrite nested objects entirely rather than deep-merging them. For example, if the existing state has {"electron-persisted-atom-state": {"a": 1, "b": 2}} and settings has {"a": 3, "c": 4}, the result will be {"a": 3, "c": 4} ("b" is lost). Consider using * (recursive merge) instead: ."electron-persisted-atom-state" = (."electron-persisted-atom-state" // {}) * $settings[0]. The current test at line 48 only validates that a specific managed key overrides an existing one, but doesn't verify that sibling unrelated keys within electron-persisted-atom-state are preserved when adding new managed keys.

Fix in Cursor • Fix in Claude

Prompt for Agent
Task: Address review feedback left on GitHub.
Repository: shunkakinoki/dotfiles#2085
File: config/codex/activate.sh#L26
Action: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.

Feedback:
The shallow merge using `+` operator will overwrite nested objects entirely rather than deep-merging them. For example, if the existing state has `{"electron-persisted-atom-state": {"a": 1, "b": 2}}` and settings has `{"a": 3, "c": 4}`, the result will be `{"a": 3, "c": 4}` ("b" is lost). Consider using `* ` (recursive merge) instead: `."electron-persisted-atom-state" = (."electron-persisted-atom-state" // {}) * $settings[0]`. The current test at line 48 only validates that a specific managed key overrides an existing one, but doesn't verify that sibling unrelated keys within `electron-persisted-atom-state` are preserved when adding new managed keys.

Comment thread config/codex/activate.sh
GLOBAL_STATE_TMP=$(mktemp "${GLOBAL_STATE}.tmp.XXXXXX")
trap 'rm -f "$GLOBAL_STATE_TMP"' EXIT

if [[ -s $GLOBAL_STATE ]]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium

Consider adding validation that $GLOBAL_STATE contains valid JSON before attempting the merge. If the file exists but is corrupted (e.g., truncated, manually edited), jq will fail and activation aborts. While fail-fast is reasonable, adding a backup step (e.g., cp "$GLOBAL_STATE" "$GLOBAL_STATE.backup" before the jq operation) would help users recover from accidental corruption.

Fix in Cursor • Fix in Claude

Prompt for Agent
Task: Address review feedback left on GitHub.
Repository: shunkakinoki/dotfiles#2085
File: config/codex/activate.sh#L23
Action: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.

Feedback:
Consider adding validation that `$GLOBAL_STATE` contains valid JSON before attempting the merge. If the file exists but is corrupted (e.g., truncated, manually edited), jq will fail and activation aborts. While fail-fast is reasonable, adding a backup step (e.g., `cp "$GLOBAL_STATE" "$GLOBAL_STATE.backup"` before the jq operation) would help users recover from accidental corruption.

@@ -0,0 +1,8 @@
{
"git-branch-prefix": "codex/",
"git-always-force-push": true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Medium

Setting git-always-force-push to true globally is dangerous and can lead to accidental data loss when multiple users collaborate on the same branch or when pushing to protected branches. Consider making this configurable per-repository or adding safeguards in the Git hooks to prevent force-pushing to specific branches (e.g., main, master, production).

Fix in Cursor • Fix in Claude

Prompt for Agent
Task: Address review feedback left on GitHub.
Repository: shunkakinoki/dotfiles#2085
File: config/codex/desktop-settings.json#L3
Action: Open this file location in your editor, inspect the highlighted code, and resolve the issue described below.

Feedback:
Setting `git-always-force-push` to `true` globally is dangerous and can lead to accidental data loss when multiple users collaborate on the same branch or when pushing to protected branches. Consider making this configurable per-repository or adding safeguards in the Git hooks to prevent force-pushing to specific branches (e.g., main, master, production).

Comment thread config/codex/activate.sh

chmod 600 "$GLOBAL_STATE_TMP"
mv -f "$GLOBAL_STATE_TMP" "$GLOBAL_STATE"
trap - EXIT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Redundant trap clear: After the successful mv -f "$GLOBAL_STATE_TMP" "$GLOBAL_STATE" on line 35, the temp file has been renamed away and no longer exists, so the EXIT trap's rm -f "$GLOBAL_STATE_TMP" is already a no-op. The script is also about to exit normally, so clearing the trap adds no safety. Consider removing this line to keep the flow simpler.

Suggested change
trap - EXIT

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.

1 participant