Skip to content

Review Codex changes with Claude Code

Review Codex changes with Claude Code by checking the worktree, comparing the full diff with the task, and running the project checks before you commit.

Riley Hilliard7 min read

On this page

To review Codex changes with Claude Code, open the checkout Codex used, inspect the diff and untracked files, then run the project’s checks before committing.

Give Claude the original task and the starting commit so it can compare the implementation with what you asked for. Ask for findings before edits. If the implementation needs substantial correction, send Codex a follow-up with the failing case, then review the revised work. You can do this with Claude Code, the Codex CLI and git; a delegation tool becomes useful when you also want a recorded hand-off and run digest.

How do you review Codex changes with Claude Code?

Start by locating the worktree. Run this from a checkout of the repository:

git worktree list

The git worktree list command shows checkout paths and their branches. Open a terminal in the path Codex used, then start Claude Code there:

cd /path/to/codex-worktree
git status --short --branch --untracked-files=all
claude

Replace the example path with the actual checkout and confirm the branch matches the task. A linked worktree has its own working files and index, so reviewing the main checkout can leave you looking at different files.

Claude Code already has a built-in /diff view for working-tree changes, including edits made outside Claude. Run /diff in the conversation to inspect Codex’s uncommitted work. In the current fullscreen panel, select the uncommitted-changes comparison and expand any collapsed files from before the session.

Ask Claude to perform a review with enough context to identify a real defect. This is an example prompt; replace the placeholders with your task and commit:

Review the Codex implementation in this checkout against the original task below. Compare tracked files with START_COMMIT, and read the untracked files listed by git status. Check the allowed paths and acceptance criteria. Report actionable bugs with file locations, the triggering case and the expected behavior. Do not edit or commit yet. Original task: [paste the task].

The OpenAI plugin’s /codex:review command goes the other direction: Codex performs the review inside Claude Code.

Which files did Codex actually change?

Use the commit from immediately before the task as the comparison base. In your shell, replace START_COMMIT with that commit’s hash:

review_base=START_COMMIT
git status --short --branch --untracked-files=all
git diff --stat "$review_base" --
git diff "$review_base" --

The single-commit form of git diff compares that commit with the working tree. It covers the tracked-file result even if some edits were staged or committed. A comparison between two commits omits uncommitted edits. Ordinary diffs also omit untracked files, so read those separately.

The status command’s --untracked-files=all option lists individual untracked files inside directories; ignored files remain excluded. Check new tests as carefully as changed implementation files. A new test may be the only place the intended behavior is written down.

If the checkout already had edits when Codex started, the diff alone cannot attribute each change to Codex. Compare it with your pre-task status or saved patch. Preserve work that belonged to someone else, and flag uncertain ownership before asking for cleanup.

foreman adds run records to this workflow. It lets Claude Code delegate to Codex in git worktrees under a local daemon, then read the run’s digest and latest available hand-off: The guide to using Codex from Claude Code compares it with the other ways to delegate.

foreman result task-name

Replace task-name with your run name. The digest includes state and model, command totals and failures, files written, paths outside the contract’s - May change: scope, new git status entries and token usage. For a foreman-created worktree, it also prints the branch and base commit. The recording below is a real site-scaffold run:

Recorded: result
$ foreman result site-scaffold

run:       site-scaffold  idle  22m32s  (gpt-sol, high, priority, write)
dir:       ~/.foreman/runs/site-scaffold
worktree:  ~/src/foreman/.claude/worktrees/site-scaffold
branch:    foreman/site-scaffold (foreman-created, base aef7e30c8438e7eeddac7099008c6e0fb51b429f)
turn:      1 (start)
question:  (none)
commands:  169 (24 failed, 0 blocked by repo hooks)
  last:    /bin/zsh -lc 'on-port 4323'  exit=1
files written:
  site/.astro/collections/blog.schema.json
  site/.astro/content-assets.mjs
  site/.astro/content-modules.mjs
  site/.astro/content.d.ts
  site/.astro/data-store.json
  site/.astro/settings.json
  site/.astro/types.d.ts
  site/.claude/README.md
  site/.env.example
  site/.gitignore
  site/.node-version
  site/.stacks/NOTES.md
  site/.stacks/state/.playwright-cli/page-2026-11-06T09-16-38-026Z.yml
  site/.stacks/state/.playwright-cli/page-2026-11-06T09-16-44-815Z.yml
  site/.stacks/state/playwright-qa/console-2026-11-06T09-03-52-977Z.log
  site/.stacks/state/playwright-qa/page-2026-11-06T09-03-53-075Z.yml
  site/.stacks/state/playwright-qa/page-2026-11-06T09-05-31-225Z.yml
  site/.stacks/state/playwright-qa/page-2026-11-06T09-05-51-816Z.yml
  site/.stacks/state/playwright-qa/page-2026-11-06T09-06-05-382Z.yml
  site/.stacks/state/playwright-qa/page-2026-11-06T09-06-05-767Z.yml
  site/.stacks/state/playwright-qa/page-2026-11-06T09-06-19-312Z.yml
  site/.stacks/state/runs.jsonl
  site/AGENTS.md
  site/Makefile
  site/README.md
  site/astro.config.mjs
  site/bun.lock
  site/docs/DEVELOPMENT.md
  site/package.json
  site/public/default-og.svg
  site/src/components/PostList.tsx
  site/src/components/posts.test.tsx
  site/src/config.ts
  site/src/content/blog/welcome.mdx
  site/src/layouts/BaseLayout.astro
  site/src/layouts/PostLayout.astro
  site/src/lib/analytics.test.ts
  site/src/lib/analytics.ts
  site/src/lib/events.ts
  site/src/pages/404.astro
  site/src/pages/blog/[...slug].astro
  site/src/pages/blog/index.astro
  site/src/pages/blog/rss.xml.ts
  site/src/pages/index.astro
  site/src/styles/global.css
  site/src/styles/sf-theme.css
  site/tests/build.test.mjs
  site/tests/deploy.test.mjs
  site/vitest.config.ts
  site/wrangler.jsonc
outside May change:  (none)
new in git status since start:
  D  site/src/pages/[...slug].astro
  D  site/src/pages/rss.xml.ts
  M  site/.claude/README.md
  M  site/.env.example
  M  site/.gitignore
  M  site/AGENTS.md
  M  site/Makefile
  M  site/README.md
  M  site/astro.config.mjs
  M  site/docs/DEVELOPMENT.md
  M  site/package.json
  M  site/public/default-og.svg
  M  site/src/components/PostList.tsx
  M  site/src/components/posts.test.tsx
  M  site/src/config.ts
  M  site/src/content/blog/welcome.mdx
  M  site/src/layouts/BaseLayout.astro
  M  site/src/layouts/PostLayout.astro
  M  site/src/lib/analytics.test.ts
  M  site/src/lib/analytics.ts
  M  site/src/lib/events.ts
  M  site/src/pages/404.astro
  M  site/src/pages/index.astro
  M  site/src/styles/global.css
  M  site/tests/build.test.mjs
  M  site/tests/deploy.test.mjs
  M  site/vitest.config.ts
  M  site/wrangler.jsonc
  ?? site/.node-version
  ?? site/bun.lock
  ?? site/src/pages/blog/[...slug].astro
  ?? site/src/pages/blog/index.astro
  ?? site/src/pages/blog/rss.xml.ts
  ?? site/src/styles/sf-theme.css
tokens:    17.1M in (99% cached), 53K out

Hand-off (turn 1):
1. **Summary**

The site foundation is green. Dependencies, shared UI styles, consent analytics, blog relocation, and Pages configuration are ready; `/` remains a placeholder. Changes are uncommitted, confined to `site/`, and nothing was pushed or deployed.

2. **Files**

All paths below are relative to `site/`.

| Files | Change |
|---|---|
| `package.json`, `bun.lock`, `.node-version` | Installed dependencies and recorded Bun/Node versions. |
| `.env.example`, `.gitignore` | Documented analytics variables; ignored Wrangler output. |
| `AGENTS.md`, `README.md`, `docs/DEVELOPMENT.md` | Filled setup, publishing, analytics, deployment, and workaround guidance. |
| `.claude/README.md` | Removed the remaining scaffold marker. |
| `Makefile`, `wrangler.jsonc` | Added deployment targets, credential checks, and compatibility date. |
| `astro.config.mjs`, `src/config.ts` | Production URL, blog sitemap paths, metadata, and one syntax theme. |
| `public/default-og.svg` | Replaced scaffold branding with foreman. |
| `src/pages/index.astro`, `src/pages/404.astro` | Placeholder landing page and home recovery link. |
| `src/pages/blog/index.astro` | Moved the blog listing under `/blog`. |
| `src/pages/[...slug].astro` → `src/pages/blog/[...slug].astro` | Relocated post routes and `getStaticPaths`. |
| `src/pages/rss.xml.ts` → `src/pages/blog/rss.xml.ts` | Relocated RSS `GET` and post links. |
| `src/content/blog/welcome.mdx` | Marked the welcome post draft. |
| `src/components/PostList.tsx`, `src/components/posts.test.tsx` | Updated and tested `/blog/<slug>` links. |
| `src/layouts/BaseLayout.astro`, `src/layouts/PostLayout.astro` | Shared consent banner, footer privacy link, metadata, and single-theme prose. |
| `src/styles/global.css`, `src/styles/sf-theme.css` | Shared UI imports, layer ordering, and marked placeholder tokens. |
| `src/lib/events.ts` | Declared the four typed event/property contracts. |
| `src/lib/analytics.ts` | Implemented `applyConsent`, site stamping, withdrawal cleanup, restoration handling, and Logs exclusion. |
| `src/lib/analytics.test.ts`, `vitest.config.ts` | Real SDK offline coverage, including remote console capture and cached-page restoration. |
| `tests/build.test.mjs` | Empty-production assertions, preserved publishing coverage, and env-aware `assertAnalyticsBundle`. |
| `tests/deploy.test.mjs` | Updated Pages routes, redirects, draft exclusion, and feed caching checks. |

Local findings and QA notes are in ignored `site/.stacks/NOTES.md`. The manifest and protected design/product/fixture paths were untouched.

3. **Decisions**

Applied the contract’s decisions. `/blog` is canonical under `trailingSlash: "never"`; Pages redirects `/blog/`, while Astro dev displays its trailing-slash diagnostic.

Documented three dependency workarounds: TypeScript 6 for checker compatibility, matching `@emnapi/runtime`, and PostHog’s session-storage cleanup/internal Logs override. Fonts are installed but unwired; theme tokens remain placeholders.

4. **Checks**

| Command | Final result |
|---|---|
| `make install`, `CI=1 make install` | Passed, including frozen lockfile. |
| `make lint` | Passed. |
| `make check` | Passed: 0 errors, warnings, or hints. |
| `make test` | Passed: 22 tests across 5 files. |
| `make coverage` | Passed: 80% statements, 81.17% lines. |
| `make build` | Passed: 5 build assertions. |
| `PUBLIC_POSTHOG_KEY=phc_test make build` | Passed; analytics/banner code present. |
| `PUBLIC_POSTHOG_KEY= make build` | Passed; analytics/banner JavaScript absent. |
| `make deploy-check` | Passed: 5 build and 5 Pages assertions. |
| `PORT_WEB=4323 stacks verify` | **Green: 12 passed, 0 failed, 0 skipped; 17.1s.** |
| `git diff --check` | Passed. |

Changed test files each passed three isolated runs: analytics 10 tests, components 3, build 5, deployment 5. The full suite passed afterward.

Also exercised `make help`, `make format`, `make clean`, `make new-article`, and `make preview`. Both manual deploy targets correctly refused missing credentials with expected exit 2.

Browser QA covered `/`, `/blog/`, canonical `/blog`, a missing URL, the dev-only welcome draft, and its hydrated read toggle. Keyed production showed the shared banner; Accept/Decline persisted across reloads. Production excludes post pages and lists only `/` in the sitemap. All QA servers are stopped.

5. **Review**

Report: [review.md](/tmp/scratch).

The single independent review reported three findings, all reproduced and resolved:

- Remote console capture bypassed `before_send`: disabled Logs and added remote-config coverage.
- Cached consent survived sibling withdrawal: added live consent gating and persisted-`pageshow` cleanup.
- Build assertions ignored env files: switched to Vite production env precedence and tested a keyed `.env.production` build.

Affected checks were rerun. No second review was performed.

6. **Look closer at**

The PostHog internal extension override and session-storage key cleanup deserve attention during SDK upgrades. Live recordings were not sent; SDK tests and browser analytics requests stayed offline. The scaffold still has no favicon.

7. **Open**

No foundation blockers or unanswered questions. External setup remains: connect Pages and the custom domain, configure the shared PostHog key, and enable cookieless tracking. Landing design, final tokens, font imports, and event callers remain with the orchestrator.
result shows the run's worktree, command failures, file lists and the agent's hand-off.

Use the digest to decide what to inspect. Its files-written scan uses modification times and skips build and cache directories, so it is not a complete audit of writes. Check the hand-off’s turn number too: if the newest turn produced none, result can show an older hand-off.

Did it stay inside the task’s scope?

Compare every changed path with the original task’s allowed files. Then check the edits within those files against the requested behavior. Permission to change a file does not justify every change inside it.

For example, a task to correct an error message might allow a command handler and its tests. A new dependency or a rewrite of the command parser needs a separate explanation. Ask Claude to identify which requirement each disputed change serves. Keep unrelated cleanup out of the patch unless you explicitly approve it.

Review deletions and weakened assertions closely. A passing test suite is less useful if the patch removed the assertion that would have caught the bug. For a bug fix, look for a test that exercises the reported failure and checks the corrected behavior. For a refactor, check that callers still receive the behavior the task required.

With foreman, scope paths are backticked entries on the - May change: line and its continuation lines. The digest reports dirty paths outside those entries. This reporting helps review; it does not restrict writes. Follow-ups retain the original contract’s scope, so give Codex corrections that fit it or handle broader work as a separate task.

How do you run the checks yourself?

Run the repository’s documented checks in the reviewed worktree, even when the agent says they passed. Use the project’s instructions to choose commands and dependency setup. For the foreman CLI repository, the documented gate is:

bun run check

That command is specific to foreman. In your project, use its own gate, including any checks the task explicitly required. Keep the exit status and failure output with your review notes. An unavailable dependency or service means a check is unverified; resolve the setup or record the remaining gap before deciding whether to commit.

Test the requested behavior directly when the automated checks leave it uncovered. For a CLI change, try the input that triggered the bug and inspect both the message and exit status. For a UI change, reproduce the affected interaction. Ask Claude to connect each check with an acceptance criterion so missing coverage is visible.

Inspect status and the diff again after verification. If a formatter rewrote files, review those edits. If a check generated an untracked file, decide whether it belongs in the patch. Any implementation fix after a successful check needs the relevant verification again.

How do you send it back for fixes?

Send a correction when the review finds a substantial problem. Include a reproducible input or failed command, the expected result, and the files the task permits. A request such as “make this cleaner” leaves the next implementation open to another round of disagreement.

For a task started with codex exec, the CLI can resume the recorded session. From the same worktree, replace the session ID and example correction:

codex exec resume SESSION_ID 'The empty-input case still succeeds. Add a regression test and return the expected usage error. Keep the original scope and leave changes uncommitted.'

For an inactive foreman run, followup starts another turn on the saved thread in the existing worktree:

foreman followup task-name 'The empty-input case still succeeds. Add a regression test and return the expected usage error. Keep the original scope and leave changes uncommitted.' --wait

After the correction, read the new hand-off and review the revised diff. Run the project checks again before staging the accepted files. foreman never commits or merges; its delegate instructions tell Codex to leave the work uncommitted and avoid pushing.

When review is complete and you are authorized to commit, stage only the accepted paths. Replace these example paths with the files you reviewed:

git add -- path/to/implementation path/to/regression-test
git diff --cached --
git status --short --untracked-files=all

git diff --cached shows the staged patch. Confirm it contains only reviewed changes, including anything staged before you started. Then use git commit with your repository’s message convention. If the task requires leaving changes uncommitted, hand over the reviewed diff and check results at this point.

Many review problems start in the brief; how to give Codex a coding task covers writing one that is easier to check.