Skip to content

fix(codex): pick up new sessions; stop misattributing active processes (#209)#215

Merged
vakovalskii merged 1 commit into
mainfrom
fix/codex-session-pickup
May 15, 2026
Merged

fix(codex): pick up new sessions; stop misattributing active processes (#209)#215
vakovalskii merged 1 commit into
mainfrom
fix/codex-session-pickup

Conversation

@NovakPAai
Copy link
Copy Markdown
Collaborator

Fixes #209.

Summary

Three related bugs in the Codex integration that hit long-running codbash servers — especially on Linux with multiple concurrent Codex sessions:

  1. New ~/.codex/sessions/**/*.jsonl files are not picked up. _sessionsNeedRescan() only watched Claude paths (~/.claude/history.jsonl, ~/.claude/projects/). New Codex sessions never invalidated the cache.
  2. /api/active cwd lookup returns garbage. On Linux lsof -a -p <pid> -d cwd -Fn can emit /proc/<n>/cwd (readlink: Permission denied); the regex /\nn(\/[^\n]+)/ captured the whole thing, then the cwd→session match failed.
  3. Multiple live Codex processes mapped to the same fallback-latest session. 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

  • Extend _sessionsNeedRescan() + _updateScanMarkers() to also track:
    • ~/.codex/history.jsonl mtime + size
    • ~/.codex/session_index.jsonl mtime + size
    • per-day mtimes of ~/.codex/sessions/YYYY/MM/DD (new _codexDayDirMtimes() helper)
  • Walk the day-dir tree exactly once per rescan check (stashed in _codexDayDirMtimesPending) and reuse the result in _updateScanMarkers() — avoids a TOCTOU race between two independent walks.
  • In getActiveSessions():
    • Prefer fs.readlinkSync('/proc/<pid>/cwd') on Linux (guarded by Number.isFinite(pid) && pid > 0).
    • lsof regex changed to /^n(\/[^\n]*)/m (multiline anchor, no leading-newline dependency).
    • Strip lsof's trailing " (readlink: …)" annotation.
    • Reject /proc/* pseudo-paths (they cannot match a real session project).
    • Among multiple sessions sharing the same cwd, pick the most recently active one (last_ts desc) instead of the first hit.
    • Drop fallback-latest when cwd is known — set sessionId = '' and _sessionSource = 'unmatched' instead of confidently misattributing.

src/frontend/app.js

  • pollActiveSessions() and renderRunningCard() now key unmatched entries (sessionId === '') by a synthetic pid:<pid> so they remain visible on the Agent Board (Focus button keeps working — focusSession uses a.pid regardless).

Test plan

  • node --test test/*.test.js → 41/42 pass; 1 pre-existing fail in wsl-windows.test.js (verified on origin/main without these changes)
  • Smoke: loadSessions() returns cached identity on repeat call; getActiveSessions() works on macOS with live Claude processes
  • Manual verify on Linux VPS with concurrent Codex sessions: new ~/.codex/sessions/2026/05/14/rollout-*.jsonl appears in /api/sessions without restart; /api/active no longer shows multiple processes all sharing the same fallback-latest session
  • _sessionSource: 'unmatched' entries appear on the Agent Board with PID-only labelling

Reviewer 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.

…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
Copy link
Copy Markdown
Owner

@vakovalskii vakovalskii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM ✅ Clean fix for #209, addresses all three root causes from the bug report:

Verified:

  • node -c clean 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: _codexDayDirMtimesPending neatly stashes the walk result inside _sessionsNeedRescan() for reuse in _updateScanMarkers() — closes the TOCTOU window cleanly without doubling FS work 👏
  • /proc/<pid>/cwd readlink guarded by Number.isFinite(pid) && pid > 0 — defensive
  • lsof regex ^n(\/[^\n]*)/m with the (readlink: …) strip + /proc/* reject handles the exact failure mode reported in #209
  • cwd-match candidate ordering by last_ts desc fixes the symptom where multiple concurrent Codex processes collapsed to one entry
  • The asymmetric fallback (unmatched when cwd known, fallback-latest only 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 in pollActiveSessions keeps unmatched entries visible without breaking focusSession (which keys on a.pid)

Thanks @NovakPAai!

@vakovalskii vakovalskii merged commit 831e528 into main May 15, 2026
6 checks passed
@vakovalskii vakovalskii deleted the fix/codex-session-pickup branch May 15, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Codex sessions: new ~/.codex/sessions files are not picked up and active process mapping falls back to latest session

3 participants