Skip to content

Worktree isolation uses process cwd instead of project worktree root #434

@ken-jo

Description

@ken-jo

Summary

context-mode's worktree isolation can choose the wrong session database when the MCP server or hook process is not running from the user's actual repository/worktree directory.

This is easy to hit with Codex/Copilot-style agents and marketplace installs because the server process can run from the installed package directory while hook stdin still contains the real project cwd.

Root cause

There are two related problems:

  1. getWorktreeSuffix() uses process.cwd() as the source of truth.

    • Installed startup code may chdir into the package/plugin directory.
    • Hooks may receive the real workspace in stdin, but the DB path helpers did not pass that cwd into worktree suffix resolution.
  2. The suffix check compares the raw cwd to the main worktree path from git worktree list --porcelain.

    • A subdirectory inside the main worktree is incorrectly treated as a linked worktree because /repo/subdir !== /repo.
    • A subdirectory inside a linked worktree gets a different hash than the linked worktree root, so the same worktree can split into multiple session DBs depending on where the hook ran.

The result is incorrect isolation: session continuity, timeline search, stats, and purge operations may read/write a DB for the package cwd or an unstable subdirectory hash rather than the current project worktree.

Expected behavior

Worktree isolation should be based on the git worktree root for the actual project directory:

  • main worktree root and any subdirectory inside it: no suffix
  • linked worktree root and any subdirectory inside it: the same stable __<hash> suffix
  • MCP server paths and hook paths should agree when the hook provides a project cwd

Proposed fix

Resolve the worktree root with:

git -C <projectDir> rev-parse --show-toplevel
git -C <projectDir> worktree list --porcelain

Then compare normalized worktree roots and hash the current worktree root rather than process.cwd().

For stdin-cwd platforms such as Codex, pass the parsed project directory into getSessionDBPath(), getSessionEventsPath(), and getCleanupFlagPath() so hooks do not fall back to the package process cwd.

I am opening a PR with this approach and regression tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions