Context
CCE works per-directory, so each git worktree gets its own index under ~/.cce/projects/<basename>/. That covers the common case, but there are two known rough edges that make worktrees feel second-class.
1. Auto-reindex git hooks don't install in worktrees
install_hooks in src/context_engine/indexer/git_hooks.py:40-50 writes to <project_dir>/.git/hooks/:
hooks_dir = Path(project_dir) / ".git" / "hooks"
if not hooks_dir.exists():
return []
In a git worktree, <project_dir>/.git is a file pointing at <main-repo>/.git/worktrees/<name>/, not a directory. The check fails silently and no hook is installed. Manual cce index still works, but post-commit/post-checkout/post-merge auto-reindex never fires from inside a worktree.
Fix: resolve the hooks dir via git itself instead of assuming layout:
result = subprocess.run(
["git", "rev-parse", "--git-path", "hooks"],
cwd=project_dir, capture_output=True, text=True, check=True,
)
hooks_dir = Path(result.stdout.strip())
if not hooks_dir.is_absolute():
hooks_dir = Path(project_dir) / hooks_dir
git rev-parse --git-path hooks returns the right directory in both regular checkouts and worktrees. Worktrees share the main repo's hooks by default, which is the right behavior — installing once from any worktree fires the hook for all of them.
2. Storage key is directory basename, not absolute path
Index storage resolves to Path(config.storage_path) / project_dir.name (see e.g. tests/indexer/test_pipeline_durability.py:38, cli.py callers). Two worktrees with the same basename — for example ~/work/myrepo and ~/scratch/myrepo — would clobber each other's index, embedding cache, and memory db.
The collision is unusual in practice because worktrees are typically given suffixed names (myrepo-feat-x, myrepo-bugfix-y), but the failure mode is silent and corrupts state when it does happen.
Fix: the codebase already solves this for editor configs in src/context_engine/editors.py:145-168 via a <basename>-<6hex> slug derived from Path.resolve(). Apply the same scheme to the storage key, with a one-time migration that moves an existing <basename>/ directory to <basename>-<hash>/ if and only if the resolved path matches.
Test plan
Context
CCE works per-directory, so each git worktree gets its own index under
~/.cce/projects/<basename>/. That covers the common case, but there are two known rough edges that make worktrees feel second-class.1. Auto-reindex git hooks don't install in worktrees
install_hooksinsrc/context_engine/indexer/git_hooks.py:40-50writes to<project_dir>/.git/hooks/:In a git worktree,
<project_dir>/.gitis a file pointing at<main-repo>/.git/worktrees/<name>/, not a directory. The check fails silently and no hook is installed. Manualcce indexstill works, butpost-commit/post-checkout/post-mergeauto-reindex never fires from inside a worktree.Fix: resolve the hooks dir via git itself instead of assuming layout:
git rev-parse --git-path hooksreturns the right directory in both regular checkouts and worktrees. Worktrees share the main repo's hooks by default, which is the right behavior — installing once from any worktree fires the hook for all of them.2. Storage key is directory basename, not absolute path
Index storage resolves to
Path(config.storage_path) / project_dir.name(see e.g.tests/indexer/test_pipeline_durability.py:38,cli.pycallers). Two worktrees with the same basename — for example~/work/myrepoand~/scratch/myrepo— would clobber each other's index, embedding cache, and memory db.The collision is unusual in practice because worktrees are typically given suffixed names (
myrepo-feat-x,myrepo-bugfix-y), but the failure mode is silent and corrupts state when it does happen.Fix: the codebase already solves this for editor configs in
src/context_engine/editors.py:145-168via a<basename>-<6hex>slug derived fromPath.resolve(). Apply the same scheme to the storage key, with a one-time migration that moves an existing<basename>/directory to<basename>-<hash>/if and only if the resolved path matches.Test plan
cce installfrom any worktree is idempotent (no duplicate marker)