codex-plusplus-reasoning-fixes · deep dive

Codex++ Reasoning Fixes

What it is, how it works, and why a Codex update forced a full rewrite from v1.1 to v1.2.

What is this? A source-patching tweak for Codex Desktop that intercepts JavaScript bundles at load time and modifies how reasoning messages, tool calls, and exploration items render. It's one of several tweaks distributed through the Codex++ plugin ecosystem.

1. What is Codex++?

Codex++ is a community plugin/tweak framework for Codex Desktop — the native macOS app for interacting with AI models. It extends Codex through a system of tweaks: self-contained packages that hook into Codex's UI lifecycle, settings system, and Electron runtime.

3
tweaks installed
12+
source patches

Each tweak lives in ~/Library/Application Support/codex-plusplus/tweaks/ and consists of two parts: a main-process source patcher that monkey-patches Electron's protocol.handle to rewrite JavaScript bundles on the fly, and a renderer UI that provides toggles in the settings panel.

2. What is the Reasoning Fixes Tweak?

The reasoning-fixes tweak (package ID co.shivam94.reasoning-fixes) addresses a set of long-standing UI annoyances in Codex's conversation view:

🧠

Standalone reasoning

Reasoning messages appear as standalone chat items instead of being nested inside exploration accordions.

split-items

No thinking shimmer

Removes the pulsing color animation on reasoning text while it's streaming — reduces visual noise.

shimmer
🚀

Auto-expand exec shells

Command outputs start expanded instead of collapsed, so you see results immediately.

thread
📋

Expand tool activity

Tool call sections (like file edits, web searches) start expanded by default.

thread
🔢

Fix assistant order

Searches the entire agent items array for an assistant message instead of assuming it's the last element.

split-items
🔍

Show exploration items

Exploration items (tool calls, file reads) appear as standalone entries in the conversation.

split-items
🔄

Collapse-all button

A floating button that toggles all reasoning sections collapsed/expanded at once.

DOM effect
📄

File edits no tool group

Prevents file-edit patches from being grouped inside tool-activity accordions.

split-items

3. How It Works

The tweak operates in two layers: a main-process source patcher that rewrites JavaScript before it reaches the renderer, and a renderer settings UI that controls which patches are active.

MAIN PROCESS (Node + Electron) RENDERER (Chromium) Source Patcher monkey-patches protocol.handle PATCHES registry 12 regex find-replace rules Codex Bundles app:// assets intercepted reads patches IPC Settings UI index.js renderer FEATURES registry runtime DOM effects Patched UI standalone reasoning, etc.

At app startup: source-patcher.js replaces Electron's protocol.handle with a wrapper. Every time Codex loads a renderer bundle (like local-conversation-thread-xxxxx.js), the wrapper intercepts the response, runs it through patchSource(), and serves the patched version.

The patch pipeline

Bundle loads inspectRule() unpatched vs patched replace() regex replacement inspectRule() verify applied Serve

Each patch is a { bundle, unpatched, patched, replacement } rule. inspectRule() runs both regexes against the source — if only unpatched matches, the patch is applied. If only patched matches, it's already done. If neither matches, Codex has changed shape and the patch is "unsupported."

4. v1.1 vs v1.2: What Changed

v1.1 was built for Codex 26.506.11943. When Codex updated, the renderer bundle shapes changed — the regex patterns in v1.1 no longer matched the new JavaScript, so all patches silently stopped applying. v1.2.0-alpha was a full re-write targeting Codex 26.506.20924, with a new patch-wiring architecture and additional patches. The fix>codex_update_26.506.20924 branch then fixed 5 bugs in that adaptation.

Version
v1.1
v1.2 (fix branch)
Codex target
26.506.11943 (older bundle shapes)
26.506.20924 → 26.506.31004
Catalyst
Codex update broke all v1.1 patches
Patches
show-reasoning disable-shimmer reasoning-no-blink collapse-all-toggle
~6 patches, names uncertain (old version not available)
auto-expand-exec expand-tool-activity fix-assistant-order show-exploration-items file-edits-no-tool-group
12 patches across 3 bundles
Bundles
Unknown — v1.1 can't be recovered
split-items, shimmer, thread
Architecture
Flat PATCHES list, manual enable/disable
SETTING_FEATURES groups, inspectRule() diagnostics, IPC sync, aggregated status API
Collapse button
Created once at startup; no runtime toggle
FEATURES entry for show/hide; idempotent creation; dynamic DOM cleanup
Diagnostics
None (patches either worked or didn't)
buildStatus() API, per-feature observations, patched-assets tracking
Bugs on landing
5 bugs wiring gaps syntax error bundle drift

Bug-fix timeline (v1.2 fix branch)

da0e3e9
Repair 3 wiring bugs
Missing SETTING_FEATURES entry, dead UI toggles from refactor, patched/replacement regex mismatch.
29810e3
Toggle now actually works
collapse-all-toggle had no FEATURES entry — button created once at startup, never toggled dynamically.
afde18c
Stale closure cleanup
Idempotency guard left cleanup variable null — switch to dynamic DOM lookup.
04f78a0
Bundle target drift
3 patches targeting wrong bundles + inspectRule used === 1 instead of >= 1.
99f25ce
Blank-screen syntax error
Replacement started with 'let' inside existing 'let' declaration — SyntaxError, blank conversation view.

Net diff: 5 commits, 2 files changed (index.js, source-patcher.js), −40/+31 net lines. The branch restored all 12 patches to working order against Codex 26.506.31004.

5. Key Fixes — Deep Dive

Click each card to expand.

!
Blank screen: let inside let

Most impactful bug. The fix-assistant-order patch injected let O=null; into existing code that already had let T=...,E=...,. Result: let X,let Y in a single comma-separated declaration = SyntaxError in strict mode. The bundle failed to parse entirely → React never mounted → white screen.

// Before (broken): let T=...,E=...,let O=null;for(...){...}let k=... // After (fixed): let T=...,E=...,O=null;for(...){...}let k=...

Lesson: A replacement correct in isolation can break the surrounding declaration. Always read 20 characters before and after the injection point.

2
Bundle drift: patches targeting wrong files

Codex updates shuffle JavaScript between bundle files. Three patches were targeting bundles where the patterns no longer existed:

PatchOld bundleActual bundle
auto-expand-execltthread
expand-tool-activitysettings-pagethread
no-layout-position-composercomposerremoved

Also fixed: inspectRule used === 1 instead of >= 1, so expand-tool-activity (matches 3×) was misclassified as "mixed" and skipped.

3
Toggle that did nothing

collapse-all-toggle appeared in the settings UI but had no FEATURES entry. When toggled, activateFeature() logged "unknown feature" and returned. The button was created once at startup — toggling had no runtime effect.

Fix: Always create the button (hidden if disabled), add a FEATURES entry to show/hide it, make creation idempotent for hot-reload safety.

4
Stale closure after idempotency guard

The idempotency guard (if (btn exists) return) was added to prevent duplicate buttons on hot-reload. But the cleanup closure captured btn — which is null when the guard fires. Cleanup silently did nothing, leaving orphan DOM elements.

Fix: Query the DOM at cleanup time (document.getElementById) instead of capturing in a closure.

5
Three wiring gaps

Three patches silently never activated because the PATCHES / SETTING_FEATURES / FEATURES registries drifted apart:

  • 1a: no-layout-position-composer defined in PATCHES but missing from every SETTING_FEATURES group
  • 1b: Two settings toggles (show-tool-outputs, disable-streaming-pulse) still advertised in the UI but their backing patches were removed in the v1.2 refactor
  • 1c: fix-assistant-order replacement and patched regex out of sync — the patched checker always returned "unsupported" even when the replacement was applied