openai / openai/codex-plugin-cc

SessionEnd cleanup fails when review cwd ≠ session cwd — broker.json looked up by cwd-hash, leaving orphan brokers even on graceful /quit

Open
#380 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
33.3k
Forks
2.3k
PR merge metrics
No merged PRs in 30d

Description

Summary

handleSessionEnd cleans up the broker by calling loadBrokerSession(input.cwd), but broker.json is stored under a cwd-derived hash (resolveStateDir(cwd)). When the broker was spawned for a different cwd than the session's cwd, the lookup misses, endpoint is null, no broker/shutdown is sent, and the broker is orphaned — even when the session exits gracefully (/quit, SIGTERM).

This is distinct from #108 (which attributes orphans to the hook not firing on crash/kill). Here the hook fires normally (exit 0) but cannot locate the broker.

Why this happens in normal use

The broker cwd is decided by resolveCommandCwd (codex-companion.mjs:149):

options.cwd ? path.resolve(process.cwd(), options.cwd) : process.cwd()

→ flows to connect(cwd)ensureBrokerSession(cwd) → broker --cwd and broker.json location.

A very common workflow breaks this:

  • Session starts in the main tree (session cwd = /repo).
  • A review runs against a git worktree — either cd worktree && node companion … or node companion … --cwd worktree → broker cwd = worktree.
  • /quit → SessionEnd input.cwd = /repoloadBrokerSession(/repo) looks at the /repo hash, never finds the worktree broker.json → broker survives.

Reviewing a worktree's uncommitted working tree requires broker cwd = worktree, so for a main-tree session this mismatch is structural, not accidental.

Reproduction (verified)

# broker registered for cwd=A
ensureBrokerSession("/tmp/probe")            # broker --cwd=/tmp/probe, alive

# SessionEnd with matching cwd  -> broker is killed (OK)
echo '{"cwd":"/tmp/probe","session_id":"s1","hook_event_name":"SessionEnd"}' \
  | node session-lifecycle-hook.mjs SessionEnd   # exit 0, broker DEAD

# SessionEnd with mismatched cwd -> hook exits 0 but broker SURVIVES
echo '{"cwd":"/tmp","session_id":"s2","hook_event_name":"SessionEnd"}' \
  | node session-lifecycle-hook.mjs SessionEnd   # exit 0, broker ALIVE  <-- bug

Observed in the wild: every live session had cwd in the main tree, while every orphan broker's --cwd pointed at a worktree — a perfect mismatch pattern.

Root-cause fix (preferred)

Decouple broker cleanup from cwd:

  • A. Track brokers by session, not cwd-hash. Record the owning session_id in broker.json (and/or a global broker index). handleSessionEnd resolves brokers by input.session_id and tears down all of them regardless of cwd. This fixes the graceful-exit mismatch directly.
  • B. Parent-session liveness watchdog in the broker. The broker records the owning session PID and periodically checks process.kill(pid, 0); if the owner is gone it self-terminates. This additionally covers SIGKILL / crash / OOM, where SessionEnd never fires at all.

A handles graceful exits; B handles abnormal exits. Together they remove the cwd dependency entirely.

Fallback (defense-in-depth, not a root fix)

An idle timeout (#108) as a last-resort net if both A and B fail. Useful but secondary — it lets brokers linger for the timeout window and masks the real ownership/lookup defect.

Related

  • #108 — broker not cleaned up on session exit / no idle timeout (overlapping symptom, different attributed root cause; the idle-timeout there is the fallback above)
  • #286 — same-cwd parallel races on broker.json (opposite case: same cwd)
  • #367 — background task hangs in a worktree cwd (worktree-specific, different symptom)

Environment

  • Plugin openai-codex/codex v1.0.4
  • Linux; reproduced directly against session-lifecycle-hook.mjs / broker-lifecycle.mjs

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with handleSessionEnd and loadBrokerSession in session-lifecycle-hook.mjs, then trace resolveCommandCwd at codex-companion.mjs:149 and broker lifecycle handling in broker-lifecycle.mjs. Reproduce the mismatched-cwd commands from the issue; done means a graceful SessionEnd locates and shuts down the broker even when its cwd differs from the session cwd.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
backend, devtools
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.