Codex Desktop (macOS): source-control watcher spawns thousands of git/sec on workspaces containing embedded git repos → syspolicyd/trustd CPU storm, fans max

Open 💬 3 comments Opened Jun 19, 2026 by 0xtootoo29

Summary

On macOS, the Codex desktop app's source-control integration runs git status continuously (file-watcher driven). When the open workspace contains embedded/nested git repositories (other repos cloned into a tracked parent repo — recorded as gitlinks, not proper submodules), the watcher recurses into every embedded repo and spawns thousands of short-lived git processes per second (≈4000/s with 18 embedded repos here). On macOS each git exec triggers Gatekeeper code-signature validation, so syspolicyd (~180%) and trustd (~100%) peg the CPU — fans max, system-wide beach-balls — while Codex's own process CPU stays low, which makes it look like a "GPU/rendering" problem.

Smoking gun / negative controls:

  • The codex CLI is unaffected (no continuous SCM watcher) → it's the desktop watcher, not git or the model.
  • A fresh/empty workspace is fine; only workspaces containing embedded repos trigger it.
  • git config diff.ignoreSubmodules all on the workspace drops git to 0/s and syspolicyd to ~5% → confirms the recursion-into-embedded-repos path is the cause.

Environment

| | |
|---|---|
| Codex Desktop | observed on 26.611.62324; since auto-updated to 26.616.32156 (repro is version-agnostic) |
| Platform | macOS 26.5.1 (25F80), Apple Silicon — Darwin 25.5.0 arm64 |
| Display | Built-in Liquid Retina XDR (ProMotion 120 Hz) |
| Model / Plan | gpt-5.5 / ChatGPT subscription (official login) |
| git | 2.50.1 (Apple Git-155) |
| Workspace | 41 GB repo with 18 embedded git repos (gitlinks, mode 160000, no .gitmodules) |

Steps to reproduce (minimal, version-agnostic)

# 1) A git repo that contains a few embedded git repos
D=/tmp/codex-nested-demo; mkdir -p "$D"; git -C "$D" init -q
echo root > "$D/root.txt"; git -C "$D" add . && git -C "$D" commit -qm init
for d in nested-a nested-b nested-c; do
  mkdir -p "$D/$d"; git -C "$D/$d" init -q; echo x > "$D/$d/x.txt"
  git -C "$D/$d" add . && git -C "$D/$d" commit -qm init
  git -C "$D" add "$d"   # git warns: "adding embedded git repository" — creates the gitlink
done
git -C "$D" commit -qm "add nested repos"

# 2) Open  /tmp/codex-nested-demo  as the workspace in Codex Desktop.
# 3) Watch git spawn rate from any terminal:
for i in $(seq 1 10); do echo "git procs: $(pgrep -x git | wc -l)"; sleep 0.3; done
#    -> stays continuously high (thousands/s with more embedded repos)
# 4) macOS `top`: syspolicyd ~180%, trustd ~100%; fans ramp.

Real-world trigger: this happens naturally when you git clone projects into a folder that is itself a git repo — git records each as a gitlink (see its own warning above).

Expected vs actual

  • Expected: the SCM watcher polls git status at a bounded rate and does not dive into every embedded repo; CPU near-idle when not editing.
  • Actual: unbounded git spawning (~4000/s) → syspolicyd/trustd CPU storm, fans, global lag.

Impact

Opening any large / monorepo-style workspace that contains nested git repos (common: a projects folder, vendored clones, tool checkouts) causes sustained 100%+ system CPU, thermal throttling, battery drain, and beach-balls across all apps — easily misread as a GPU issue since the Codex process itself looks idle.

Local diagnostics

  • pgrep -x git | wc -l → ~4000/s; lsof -d cwd <git-pid> → all running in the workspace root.
  • top: syspolicyd ~180%, trustd ~100%, WindowServer high; quitting Codex → ~0.
  • /usr/bin/sample <Codex pid> → main thread in V8/JS + uv__fsevents (libuv file-watch).
  • A single git status on the workspace is ~0.18 s → the problem is spawn rate × N embedded repos, not slow git.

Root-cause hypothesis

The desktop SCM watcher re-runs git status (incl. per-submodule status) on a tight, file-event-driven loop with no debounce and no embedded-repo/depth guard. With N embedded gitlinks it spawns ≥N git children each pass; on macOS the per-exec Gatekeeper validation turns that into a syspolicyd/trustd storm.

Workaround (confirmed)

On the workspace repo: git config diff.ignoreSubmodules all (+ git config core.untrackedCache true) → git 0/s, syspolicyd ~5%. Cleaner structural fix: untrack the embedded repos (git rm --cached <dir>) and .gitignore them so they stay independent repos the parent ignores.

Suggested direction

  • Debounce/coalesce the SCM git status polling; cap concurrent git children.
  • Don't recurse into embedded/nested repos for status; honor diff.ignoreSubmodules.
  • Add a scan-depth / exclude setting (prior art: VS Code git.repositoryScanMaxDepth).

Related (searched, appears distinct)

#27570 / #27382 (nested repos, different code paths) and #11524 (macOS git→syspolicyd, venv trigger) — filing the macOS desktop SCM-watcher + embedded-repo + syspolicyd combination as a distinct, instrumented report.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗