Skip to content

Submodule visibility gap and poll-interval scaling in git-strategy watcher #71

Description

@halindrome

Summary

The git-strategy watcher in internal/watcher/watcher.go has two related problems that compound for large projects with git submodules.

Problem 1: Submodule working-tree changes are invisible until committed

The gitSentinel function runs git status --porcelain on the project root:

cmd := exec.CommandContext(ctx, "git", "--no-optional-locks", "-C", rootPath,
    "status", "--porcelain", "--untracked-files=normal")

For a repo with submodules, this shows submodule directories as modified only after a commit is made inside the submodule (causing the submodule HEAD to diverge from what the parent records). Uncommitted edits to files inside a submodule are invisible to the parent's git status — the parent only sees the submodule as a single opaque directory entry.

This means:

  • Agent edits files in apps/mobile/ → parent git status shows nothing → no reindex triggered
  • Agent commits in apps/mobile/ → parent git status shows apps/mobile as modified → reindex eventually triggered (up to 60s later)
  • Unstaged changes inside submodules are never visible to the watcher

Problem 2: Poll interval backs off to 60s for large repos

The adaptive interval formula:

func pollInterval(fileCount int) time.Duration {
    ms := 5000 + (fileCount/500)*1000
    if ms > 60000 { ms = 60000 }
    return time.Duration(ms) * time.Millisecond
}

A monorepo with 6 submodules can easily have 25,000+ files, which pushes the interval to the 60-second cap. Combined with Problem 1, this means:

  • Uncommitted changes in submodules: never detected
  • Committed changes in submodules: up to 60s delay before reindex

Suggested improvements

Option A: Add --recurse-submodules to gitSentinel

cmd := exec.CommandContext(ctx, "git", "--no-optional-locks", "-C", rootPath,
    "status", "--porcelain", "--untracked-files=normal", "--recurse-submodules")

This makes working-tree changes inside submodules visible to the parent. The downside is that git status --recurse-submodules is significantly slower for large repos.

Option B: Run per-submodule git sentinels

Parse .gitmodules and run a separate gitSentinel for each submodule path. Allows the same efficient git status per submodule without recursion overhead. More complex to implement.

Option C: Signal / touch mechanism

Provide a way for external processes (hooks, scripts) to signal the daemon that a reindex is needed immediately — e.g., a sentinel file path or a lightweight HTTP endpoint. This would allow Claude Code hooks to trigger reindexing on demand without waiting for the next poll cycle.

Context

This was discovered while building a Claude Code hook layer on top of codebase-memory-mcp for a monorepo with 6 submodules (mobile app, web app, REST API, SSE service, chatbot orchestrator, MCP server). The 60-second lag and submodule blindspot make the auto-sync feature unreliable for this use case — we work around it by calling index_repository explicitly after commits, but the gap is still visible.

Environment

  • codebase-memory-mcp: latest (reviewed source at commit on main)
  • Project type: git repo with 6 submodules, ~25k+ files
  • Watch strategy selected: git (git repo detected correctly)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions