Skip to content
Productivity
Agent

codex-pre-edit-tracking-investigation

Status: **STUCK**. Three approaches tried, none reliably solves the pre-edit race for `update`-kind file_change items. This doc captures everything learned so the next session can pick up cleanly without re-deriving.

From plugin
nimbalyst
2.1k3 skills3 agents54 commands3 MCP

Claude Code

> /plugin marketplace add nimbalyst/nimbalyst
> /plugin install nimbalyst-wiki@nimbalyst

Agent definition

codex-pre-edit-tracking-investigation.md

Codex Pre-Edit Tracking — Investigation State

Status: **STUCK**. Three approaches tried, none reliably solves the pre-edit race for `update`-kind file_change items. This doc captures everything learned so the next session can pick up cleanly without re-deriving.

Tracker: **NIM-586** ("Codex sessions miss post-edit snapshot; sidebar diff peek uses git instead of session history") — the part about post-edit snapshots is solved. The pre-edit race for updates is still open.

The user-visible problem

In the **FilesEditedSidebar peek popover**, AI-edited files render as all-green (entire file looks added) instead of a real red-green diff. Reproduces 100% for any file the AI touches that is:

  • gitignored (e.g. anything under `/tests/` in this repo)
  • untracked in git
  • brand-new in a worktree

The peek calls `git:file-diff` with `group: 'working'` (see `FilesEditedSidebar.tsx:162-170`). For files not in HEAD, the handler falls through to `git diff --no-index -- /dev/null <file>` which produces an all-added unified diff. See `GitHandlers.ts:879-905`.

What we shipped this session (works, do not regress)

1. Codex SDK upgrade 0.128.0 → 0.130.0

  • Bumped in `packages/runtime/package.json:206` and `packages/electron/package.json:61`
  • npm lockfile had to be manually patched: 16 codex-related entries under `packages/electron/node_modules/@openai/` and `packages/runtime/node_modules/@openai/` were stale. Deleted those entries via a Node script, ran `npm install`, codex-sdk hoisted to root `node_modules/@openai/codex-sdk@0.130.0`.
  • Verified native binary present at `node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin`
  • `peer: true` count unchanged (17 → 17)

2. Post-edit snapshot pipeline (NIM-586 part 1)

Codex's `file_change` `item.completed` now triggers a `post_edit_snapshot` chunk that writes an `ai-edit` history row tagged with `sessionId` in metadata. Mirrors Claude's `AgentToolHooks.createTurnEndSnapshots`.

  • `StreamChunk.type` extended with `'post_edit_snapshot'` in `packages/runtime/src/ai/server/types.ts:388, 460-477`
  • `OpenAICodexProvider.maybeBuildFileChangePostEditSnapshot` at `OpenAICodexProvider.ts:~2110` — fires on `item.completed` for `file_change`, reads each affected path from disk, reuses the synthetic edit-group ID via `lookupCodexEditGroupId`, skips `delete` kinds.
  • Yielded in main loop at `OpenAICodexProvider.ts:~1010` right after pre-edit yield, wrapped in try/catch.
  • Handler in `MessageStreamingHandler.ts:~1329` writes via `historyManager.createSnapshot(absPath, content, 'ai-edit', desc, { sessionId, toolUseId })`.
  • Verified: 3 pre-edit + 3 ai-edit rows landed for test session, all stamped with same `toolUseId = nimtc|item_15|...`

3. Session-aware diff IPC (NIM-586 part 2)

`session:file-diff(workspacePath, sessionId, filePath)` IPC handler synthesizes a unified diff from pre-edit baseline (red) vs ai-edit snapshot (green), falling back to current disk if no `ai-edit` snapshot exists. Returns `{ unifiedDiff, isBinary, source }`.

  • Handler at `packages/electron/src/main/ipc/SessionFileHandlers.ts:~221`
  • New helper `historyManager.getLatestSnapshotContent(filePath, sessionId, snapshotType)` at `HistoryManager.ts:~1163`
  • `FilesEditedSidebar.handleGetDiff` (`FilesEditedSidebar.tsx:162-191`) tries the session-aware IPC first when `activeSessionId` is set; falls back to `git:file-diff` if no session baseline exists.
  • Verified via `renderer_eval`: returns 398-byte unified diff for a `change-tracking-codex-test.md` (new file) with `source: 'session-history'`.

4. Codex PreToolUse hook plumbing (LOADED BUT NOT WORKING)

All the wiring is in place but **codex is not honoring the inline `--config hooks.PreToolUse=[{...}]` override**. Evidence: `[CODEX] PreToolUse hook configured` log fires at session start (confirming resolver returns valid path and config-builder runs), but the sidecar dir is never created (confirming the hook subprocess never runs).

Files involved:

  • `packages/electron/resources/codex-pre-edit-hook.mjs` — Node script. Reads stdin payload, extracts paths from apply_patch DSL (`*** Add/Update/Delete File:`), snapshots each path's content to `<NIMBALYST_PRE_EDIT_DIR>/<sha1(path)>.json`. Always emits `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}`. **Verified working when invoked directly via `ELECTRON_RUN_AS_NODE=1 <electron> <hook.mjs>` with stdin payload.**
  • `packages/electron/src/main/services/ai/codexPreEditHookPath.ts` — resolves the script path in dev (via `getPackageRoot() + resources/`) and packaged (via `process.resourcesPath`).
  • `packages/electron/src/main/index.ts:~1429` — registers `OpenAICodexProvider.setPreEditHookScriptPathResolver(resolveCodexPreEditHookScriptPath)` and `setPreEditSidecarDirResolver(sessionId => userData/codex-pre-edit-snapshots/<safeId>)`.
  • `OpenAICodexProvider.ts`:
  • New static fields `preEditHookScriptPathResolver` and `preEditSidecarDirResolver` plus setters
  • `buildCodexConfigOverrides` injects `configOverrides.hooks = { PreToolUse: [{ matcher: '^apply_patch$', hooks: [{ type: 'command', command: '"<execPath>" "<hookPath>"' }] }] }`
  • `sendMessage` adds `NIMBALYST_PRE_EDIT_DIR` and `ELECTRON_RUN_AS_NODE=1` to `codexEnv` per session
  • `maybeBuildFileChangePreEditSnapshot` reads from sidecar (`<dir>/<sha1(path)>.json`) before falling back to disk-read
  • `packages/electron/package.json` — added `resources/codex-pre-edit-hook.mjs` to `extraResources`

The pre-edit race (the actual bug)

Codex's apply_patch emits `item.started` for `file_change` **at the same time or slightly after** it applies the patch to disk. When `OpenAICodexProvider.maybeBuildFileChangePreEditSnapshot` does `fs.readFileSync(filePath)` at `item.started` time, the read often captures **post-edit** content because the patch already wrote.

The host's existing fallback (use `FileSnapshotCache` from `HooklessAgentFileWatcher`) only helps when chokidar had time to

Read more
@nimbalyst
Ships withnimbalyst

Nimbalyst - The open-source visual workspace for Claude Code, Codex, and OpenCode. Run multiple coding agents in parallel, edit their work visually in markdown, mockups, and diagrams, and track tasks. Free, MIT-licensed desktop app for macOS, Windows, Linux, with mobile companion for iOS and Android.

2.1k16 skills3 agents3 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
2.1k
Star history of nimbalyst/nimbalyst on GitHub, a chart by star-history.com

Other agents on nimbalyst.

e2e-runner

e2e-runner

Agent

Run E2E tests in a dev container for isolated, reproducible test execution. Use proactively when asked to run Playwright tests,…

@nimbalyst@nimbalystInstall Agent

More agents from other plugins.

cto-p10

cto-p10

Agent

P10 CTO/架构委员会 Agent。定义技术战略方向、组织 agent 团队拓扑、建设基础能力。当面对超大型项目(5+ agents, 3+ sprints)、需要战略级架构决策、或需要跨多个 P9 协调时使用。触发词:CTO…

pua2mo20k
@tanweai@tanweaiInstall Agent
verifier
Auto-invoked

verifier

Agent

Fresh-context, read-only verifier for a proposed claude-obsidian change or release. Inspects the requested staged diff, unstaged…

inbox-zero-api-cli

inbox-zero-api-cli

Agent

Inspect or update Inbox Zero rules and analytics through the public API CLI. Use when tasks involve rules, stats, or API-driven…

@elie222@elie222Install Agent

Questions about codex-pre-edit-tracking-investigation.

Which coding agents can use codex-pre-edit-tracking-investigation?
codex-pre-edit-tracking-investigation installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.