Repository navigation
auto_watch is session-scoped and keyed by path-derived name — override-named indexes never refresh, and a watching session silently forks a duplicate index #1660
Description
Environment
- Version:
codebase-memory-mcp 0.10.3(--version), single Mach-O arm64 binary at~/.local/bin. - OS: macOS (Darwin 25.6.0), daemon under launchd (
daemon start,KeepAlive=false). - Config (
config list):auto_watch = true,auto_index = false,auto_index_limit = 50000,ui-lang = auto.
Summary
Two coupled behaviors make auto_watch=true fail to keep an index fresh in exactly the setups where
background freshness matters most — headless/CI clients, subagent-only workflows, and any project indexed
under a --name override:
- The watcher is session-scoped. The background git watcher is registered only when an interactive
MCP client connects (watcher.watch) and torn down on disconnect (watcher.unwatch). A print-mode /
one-shot client acquires no watch, ephemeral subagents share a parent bridge and hold none, and a project
with no live interactive session goes stale indefinitely — despiteauto_watch=true. - The watch keys by the path-derived name, never the
--nameoverride. An index stored under an
override name receives zerowatcher.watchevents (orphaned/stale). Worse: when a real session later
opens the same path, the daemon creates a second full index under the derived name instead of
refreshing the override-named one — a silent duplicate (2× disk, and the original stays stale).
The server-advertised contract "once indexed, watched projects auto-refresh in the background" silently does
not hold for these cases.
Reproduction (minimal)
# 1. Index a repo under an override name.
codebase-memory-mcp cli index_repository --repo-path /path/to/repo --name my-alias
# 2. Edit + commit a tracked file in /path/to/repo.
# 3. Inspect — 'my-alias' is never refreshed by the watcher:
codebase-memory-mcp cli index_status --project my-alias # stale; mtime unchanged
# 4. Open an INTERACTIVE MCP session with cwd=/path/to/repo (auto_watch=true), idle.
# 5. Daemon log now shows a watch on the DERIVED name + a brand-new index:
# watcher.watch project=<path-derived-name> path=/path/to/repo
# -> project '<path-derived-name>' is created & indexed; 'my-alias' STILL stale.<path-derived-name> = the path with separators flattened, e.g.
/Users/me/code/project-a → Users-me-code-project-a.
Evidence (live 0.10.3 daemon)
A. Every watch binds the derived name; override names are never watched. grep -c over the daemon log
(project names anonymized; counts verbatim):
watcher.watch project=… |
occurrences |
|---|---|
alpha (override name) |
0 |
beta (override name) |
0 |
Users-me-code-project-a (derived, same repo as alpha) |
6 |
Users-me-code-project-b (derived) |
31 |
Sample line — the project key is always the flattened path, the --name never appears:
level=info msg=watcher.watch project=Users-me-code-project-a path=/Users/me/code/project-a
The override-named project (alpha) sat stale for ~2 days until a manual index_repository.
B. The duplicate index, on disk. Two coverage reports for the SAME repo, ~1 min apart, identical
parse_partial=11 file set — one under the derived name, one under the override name:
logs/Users-me-code-project-a-<t1>.log # 02:39:29 project=Users-me-code-project-a
logs/alpha-<t2>.log # 02:40:42 project=alpha
C. Session lifecycle is the trigger. A steadily-used project (project-b above) shows 31 watcher.watch
interleaved with watcher.unwatch — connect/disconnect cycling — confirming the watch lives and dies with
the interactive session, not the project.
D. config --help documents the design as session-scoped:
auto_watch default=true Register background git watcher on session connect
Impact
- Headless / CI / print-mode (
-p) clients: never fresh. - Subagent-only workflows: never fresh (shared parent bridge holds the only watch, if any).
- Any
--name-overridden project: never fresh and silently duplicated when a session opens its path. - Users read "watched projects auto-refresh" and trust a stale index. The duplicate wastes disk and hides
the staleness behind a fresh-looking twin under a different name.
Requested changes
-
Persistent
watch_projectsconfig (or awatch add/list/removesubcommand). A declared set of repo
roots the daemon watches unconditionally at startup, independent of any client session. Decouples
freshness from an interactive session and fixes the headless/subagent/CI case. Todayconfigexposes only
4 keys — none declare a standing watch. -
Root-path-matched watch binding (the core fix). Resolve the watch target — and the index it
refreshes — by canonical repo root path, not by display name. If a project already exists for that
root under any name, refresh THAT project; never create a derived-name twin for an already-indexed root.
Keep--nameas a pure display alias over the path identity. This single change eliminates both the
orphaning (#A) and the duplicate (#B). -
A rename / alias tool.
rename_project --project <old> --name <new>(or analias) so an existing
index can be renamed to the derived name / carry an alias without today's index-new → verify → delete-old
migration dance. -
Document + retune the hook-augment deadline. The SessionStart/SubagentStart hook-augment enforces a
hard 2000 ms default deadline; on cold subagent starts it silently times out — 16
hook-augment: deadline_exceeded ms=2000events in a single session — starving subagents of graph
context (frequently misread as "subagents don't use CBM"). The log line itself points to
CBM_HOOK_DEADLINE_MS, but the knob is undocumented. Please (a) document the env var, (b) raise the
default (≈4000 ms), and/or (c) make hook-augment non-blocking so a slow augment degrades to "no extra
context" without affecting the turn.
Happy to test a patch against this install and report back with the same log-grep evidence.
Activity
DeusData commentedon Aug 18, 2026
Thank you for the measured watcher lifecycle and duplicate-index evidence. For triage, this needs to remain an umbrella rather than become one implementation PR because it contains four independent surfaces:
The root-identity correctness bug is the high-priority core. The other three require separate product and API decisions and should be tracked as focused follow-ups. Any PR against this umbrella should address only one of those boundaries. Thank you for collecting the evidence that connects them without hiding their different review costs.
DeusData commentedon Sep 26, 2026
Thank you, @7hr1LL, for the measured watcher lifecycle! One part of the root-identity bug now has a fix up. #2344 changes an unnamed
index_repositoryon a root that was first indexed under an explicit name: it now updates that project in place instead of forking a second index, and if more than one project owns the root, it fails loudly and names them.That doesn't yet cover the session path you described. Session auto-index and watcher registration still key on the path-derived name, so the orphaned override-named index is still open there. It stays the high-priority core of this umbrella. The persistent-watch, rename and hook-deadline items remain separate decisions, as noted before.
njuptlzf commentedon Sep 29, 2026
Small refinement from 0.11.0 / Windows, in case it narrows the fix:
In my measurements it is not "an interactive MCP client connects" that registers the watch, but specifically the client that creates the session-managed daemon. A second, fully interactive client that joins the already-active daemon registered nothing for its cwd (
watcher.watch/baseline/changed/unwatch= 0/0/0/0), anddaemon stopis refused while any client holds it (daemon: NOT stopped — 1 committed client(s) still use it).Supporting detail from one log:
daemon.start580 vsdaemon.lifetime_end579 (reason=runtime_exited) — each watch lives only for its creating client's daemon lifetime. Same log, per-project watch counts: session workspace 598, a project repeatedly indexed by CLI 118, the nested repo actually being edited 0.Fuller write-up with numbers on #1429.
fix(mcp): resolve a directory project arg to its owning project (#1690)
DeusData commentedon Oct 19, 2026
Thank you for the Windows follow-up distinguishing the daemon-creating client from clients that join it. That narrows the remaining session/watch registration case beyond the earlier unnamed-index change in #2344. I have no verified fix for that case to report yet; the persistent-watch and alias proposals still need separate decisions.
DeusData commentedon Oct 25, 2026
Thank you for the Windows clarification about clients joining an existing daemon. The session/watch binding case remains outstanding beyond the earlier unnamed-index change. Sorry for the delay; this is waiting on maintainer follow-up, and I’ll follow up soon.