Use this workflow when changing Codex Plus plugins or patch injection points. Transform tests are necessary, but runtime UI work is not done until the patched app is launched and checked through DevTools.
Apply patches to a workspace-local app under work/:
codex-plus-patcher apply \
--mode dev \
--patch-dir ./src/patches \
--target "work/Codex Plus.app"Sync a private development CODEX_HOME that snapshots thread sqlite state and
shares the original worktrees and sessions:
codex-plus-patcher dev-syncLaunch the copied app with private Electron user data and a remote debugging port:
codex-plus-patcher launch-dev \
--target "work/Codex Plus.app" \
--remote-debugging-port 9234launch-dev also gives the copied bundle a dev-only app identity by default
(com.openai.codex-plus.dev). Use --dev-instance-id <id> when running more
than one workspace-local copy at the same time. This is the preferred
lightweight sandbox for plugin work: private sqlite state, private Electron
user data, a separate remote debugging port, and a distinct macOS app identity
without needing a VM.
Open the DevTools target at http://127.0.0.1:9234/json/list and attach to the
page whose URL is app://-/index.html.
Dev mode intentionally shares real worktrees while keeping sqlite and Electron user data private. Use it for plugin and UI validation, not for starting concurrent turns or edits against the same checkout from both apps.
Production Codex and production Codex Plus cannot safely run side-by-side. Both
use the same default ~/.codex state, including sqlite databases and their WAL
and lock files. When both apps open those databases, sqlite locking can prevent
startup or leave one app waiting on the other. The dev launch avoids that clash
by giving Codex Plus a private CODEX_HOME and Electron user data directory
while symlinking worktrees/ and sessions/ back to the original Codex home.
Run the reusable live audit before declaring plugin work complete:
codex-plus-patcher audit-pluginsThe audit applies the current patch set to work/Codex Plus.app, syncs the
default dev home, launches with a remote debugging port, attaches to
app://-/index.html, and prints human-readable progress by default. On a TTY,
spinners show the active major phase while completed phases remain as stable
lines. Patch application includes its apply steps and names the selected patch
set and patches; probing names the plugins being checked. Redirected progress
uses timestamped plain lines without terminal control sequences.
It exits nonzero when any required built-in plugin probe fails.
Audit launches use the dev-only com.openai.codex-plus.audit bundle identity
by default so they do not compete with a kept-open manual dev copy.
Use compact JSONL progress for long-running audits and agent supervision:
codex-plus-patcher audit-plugins --jsonlIn this mode stdout contains JSONL only, including failures. Active work emits
a low-noise status record at least every two seconds with its phase, elapsed
time, and current patch or plugin context when known. There are no spinners or
other terminal adornments. The last record is a compact summary.
Add --json when you need the full final result and detailed post-failure
probe data:
codex-plus-patcher audit-plugins --jsonThis keeps human progress and prints the detailed JSON result at the end. It
can also be combined with --jsonl; in that case the final line is a JSONL
result record containing the detailed result:
codex-plus-patcher audit-plugins --jsonl --jsonPlugin audits write a visual contract by default under
work/audit-plugins/<timestamp>-<version>/, including contract.json,
audit-summary.json, and screenshots for shell/sidebar, Review,
command-palette dispatch, and Settings. Pass --no-visual-contract only when
that proof is deliberately unnecessary.
Pass --keep-open to leave the workspace-local audit app running for manual
DevTools inspection after the probes finish. The default audit avoids opening
extra native windows so the app remains usable after a keep-open run. Use
--include-native-open-probes when you specifically want the audit to open
DevTools and a Mermaid viewer window as part of the live probes.
For a manual checkpoint, use audit-plugins --manual. Manual mode skips the
probes, keeps the app open, and still prepares the generated fixture. Do not
use a dev-sync live-state launch for regression review unless live source
state was explicitly requested.
When an audit app is already open, attach instead of relaunching:
codex-plus-patcher audit-plugins --no-apply --no-launch --port 9234If a true clean-room check is needed, use a VM or separate macOS user account, but that is heavier than the normal dev workflow and should not be necessary for ordinary plugin validation.
- Plugins: in the renderer target, check
window.CodexPlus.plugins.list().map((plugin) => plugin.id)and confirm every built-in plugin is started. - Commands: check
window.CodexPlus.ui.commands.commandMetadata()forcodexPlusToggleSidebarNameBlur,codexPlus.focusProjectSelector, andcodexPlusOpenDevTools. - Project colors: inspect sidebar project rows, child thread rows, the active
thread, user bubbles, and the composer. Their computed
--codex-plus-project-accentvalues should match for the selected project. - Project selector: open the selector, type a fuzzy query such as
hdev, and verify ranking, match highlights, and Enter-to-first-result. - Sidebar blur: run
window.CodexPlus.commands.run("codexPlusToggleSidebarNameBlur")and confirm sidebar project and chat names receivefilter: blur(4px). - Mermaid fullscreen: render a Mermaid diagram, open the fullscreen viewer, and
confirm the current
mermaid.core-*asset loads. - DevTools bridge: run
window.CodexPlus.native.request("devtools/open")and confirm it resolves to{ ok: true }. - Nested repositories: request nested repository targets through the worker bridge and confirm subrepositories appear in the Review pane.
- Dev sqlite isolation: confirm sqlite handles point under
work/codex-plus-dev-home, bothstate_5.sqliteandsqlite/state_5.sqliteare snapshots when present, andworktrees/plussessions/inside the dev home are symlinks to the original Codex home. - Startup cleanliness: after launch, the initial composer must be empty. Text
left from a command search, such as
blur, is a defect.
For a new port or shared host-hook change, keep the proof loop short and ordered:
- Add the exact newest patch and version-owned transform variants.
- Run focused transform tests.
- Run the newest-only in-memory preflight.
- Run the newest live regression and inspect all four contract screenshots.
- Run all-version preflight.
- Run the affected-version live sweep and inspect every selected contract.
Use these commands for steps 3 through 6:
rtk node scripts/regression-sources.js --preflight-only --newest 1 --jsonl
rtk node scripts/regression-sources.js --newest 1 --jsonl
rtk node scripts/regression-sources.js --preflight-only --jsonl
rtk node scripts/regression-sources.js --affected-since <base-commit> --auto-clean --jsonlThese direct Node commands are required for agent supervision under RTK because the npm route buffers its child output. Continue reading the JSONL stream while the command runs; active phases report at least every two seconds. Interrupting the command stops the sweep before the next source and cleans up the active audit application.
Affected selection is fail-closed. Additive version-only ports select the new
versions. Addition-only shared-transform hunks stay local only when every hunk
is explicitly guarded by a newly registered transform owner. Audit or fixture changes also select the newest supported source from
each source family. Shared runtime, host adapter, public API, patch-engine,
hook, existing registry, or unclassified application changes select every
supported version. The runner writes an ignored impact-summary.json with the
resolved base SHA and a reason for every selected or skipped source.
--affected-since is deliberately incompatible with --preflight-only so the
complete cached-source preflight cannot be narrowed.
Restart from focused tests after any implementation change. For an older failure, compare its owned transform and file mapping with the Git diff before changing the hook. If neither changed, investigate the preflight rule, generated fixture, live audit, and screenshot state first.
Start from the live UI and the patched ASAR, not from guesses. Inspect the DOM, React props where visible, and the minified bundle around the rendered element.
Prefer hook points in this order:
- Existing prop or object boundaries.
- Stable data-attribute boundaries.
- Generic host adapters such as
CodexPlus.ui.*,CodexPlus.commands.*, andCodexPlus.native.*. - Narrow imports or requires that load readable runtime code.
Reject React compiler cache branches, cache sizes, and t[...] writes as hook
points unless there is no smaller current hook. If one is unavoidable, add a
test that proves the anchor fails closed and write down the reason near the
patch.