fix(codex): pick up new sessions; stop misattributing active processes (#209)#215
Merged
Conversation
…sses (#209) - Watch ~/.codex/history.jsonl, session_index.jsonl and per-day session dirs in _sessionsNeedRescan so new Codex sessions appear without restarting a long-running codbash server. Walk is performed once per rescan and reused in _updateScanMarkers to avoid a TOCTOU race. - Prefer /proc/<pid>/cwd readlink on Linux; harden lsof fallback with a multiline regex (no leading-newline dependency), strip "(readlink: …)" annotations, and reject /proc/* pseudo-paths. - Drop fallback-latest when cwd is known: emit sessionId='' and _sessionSource='unmatched' instead of confidently misattributing the process to an unrelated latest session. Pick the most recently active session among same-cwd candidates. Frontend now indexes unmatched entries by 'pid:<pid>' so they remain visible on the Agent Board. Accepted lower-severity review notes: - lsof multiline edge cases: -d cwd already constrains output to one FD - mid-walk day-dir deletion: next poll rescans, throw is swallowed - EACCES on ~/.codex: symmetric with other agents' silent skip - stat-on-poll when no Codex installed: one existsSync, negligible
vakovalskii
approved these changes
May 15, 2026
Owner
vakovalskii
left a comment
There was a problem hiding this comment.
LGTM ✅ Clean fix for #209, addresses all three root causes from the bug report:
Verified:
node -cclean on both touched files- 40/42 tests pass, 1 skip (HOME-as-git-root from #212), 1 fail (wsl-windows, pre-existing on main)
- CI green 6/6
Spot-checks:
- Codex cache invalidation:
_codexDayDirMtimesPendingneatly stashes the walk result inside_sessionsNeedRescan()for reuse in_updateScanMarkers()— closes the TOCTOU window cleanly without doubling FS work 👏 /proc/<pid>/cwdreadlink guarded byNumber.isFinite(pid) && pid > 0— defensive- lsof regex
^n(\/[^\n]*)/mwith the(readlink: …)strip +/proc/*reject handles the exact failure mode reported in #209 - cwd-match candidate ordering by
last_tsdesc fixes the symptom where multiple concurrent Codex processes collapsed to one entry - The asymmetric fallback (
unmatchedwhen cwd known,fallback-latestonly when cwd absent) is the right call — better to surface an honest 'we don't know' on the Agent Board than confidently misattribute - Synthetic
pid:<pid>key inpollActiveSessionskeeps unmatched entries visible without breakingfocusSession(which keys ona.pid)
Thanks @NovakPAai!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #209.
Summary
Three related bugs in the Codex integration that hit long-running
codbashservers — especially on Linux with multiple concurrent Codex sessions:~/.codex/sessions/**/*.jsonlfiles are not picked up._sessionsNeedRescan()only watched Claude paths (~/.claude/history.jsonl,~/.claude/projects/). New Codex sessions never invalidated the cache./api/activecwd lookup returns garbage. On Linuxlsof -a -p <pid> -d cwd -Fncan emit/proc/<n>/cwd (readlink: Permission denied); the regex/\nn(\/[^\n]+)/captured the whole thing, then the cwd→session match failed.fallback-latestsession. When cwd was known but no session matched it, the code still confidently attributed the process to "the latest session of this tool".Changes
src/data.js_sessionsNeedRescan()+_updateScanMarkers()to also track:~/.codex/history.jsonlmtime + size~/.codex/session_index.jsonlmtime + size~/.codex/sessions/YYYY/MM/DD(new_codexDayDirMtimes()helper)_codexDayDirMtimesPending) and reuse the result in_updateScanMarkers()— avoids a TOCTOU race between two independent walks.getActiveSessions():fs.readlinkSync('/proc/<pid>/cwd')on Linux (guarded byNumber.isFinite(pid) && pid > 0)./^n(\/[^\n]*)/m(multiline anchor, no leading-newline dependency)." (readlink: …)"annotation./proc/*pseudo-paths (they cannot match a real session project).last_tsdesc) instead of the first hit.fallback-latestwhen cwd is known — setsessionId = ''and_sessionSource = 'unmatched'instead of confidently misattributing.src/frontend/app.jspollActiveSessions()andrenderRunningCard()now key unmatched entries (sessionId === '') by a syntheticpid:<pid>so they remain visible on the Agent Board (Focusbutton keeps working —focusSessionusesa.pidregardless).Test plan
node --test test/*.test.js→ 41/42 pass; 1 pre-existing fail inwsl-windows.test.js(verified onorigin/mainwithout these changes)loadSessions()returns cached identity on repeat call;getActiveSessions()works on macOS with live Claude processes~/.codex/sessions/2026/05/14/rollout-*.jsonlappears in/api/sessionswithout restart;/api/activeno longer shows multiple processes all sharing the samefallback-latestsession_sessionSource: 'unmatched'entries appear on the Agent Board with PID-only labellingReviewer notes
The review (code-reviewer agent) surfaced 2 HIGH (double walk / Agent Board invisibility) — both fixed. 3 MEDIUM and 2 LOW accepted with reasons documented in the commit body.