force=true re-index produces wildly inconsistent garbage file/chunk counts
Bug description
index_codebase with force=true produces wildly inconsistent garbage results — each call returns a different, nonsensical file/chunk count that does not reflect the actual content of the codebase being indexed. The clear_index + index_codebase (no force) path on the same codebase consistently produces the correct count.
This is NOT the same bug as #295 (which was about snapshot 0/0 corruption causing an infinite reindex loop, fixed in v0.1.7). The bug here is that force=true produces incorrect indexing results — different file/chunk counts on every run, with the indexed content being junk (.venv/, .pytest_cache/, __pycache__/, .egg-info/, etc.) that the codebase's .gitignore explicitly excludes.
Reproduction steps
I ran this on the media-cli codebase (a small Python project, 57 git-tracked files: 31 .py + 18 .md + Makefile + pyproject.toml + scripts + tests + arr_cli/ package + .specs/ docs).
The codebase's .gitignore explicitly excludes:
.venv/,venv/,env/,ENV/.pytest_cache/,.coverage,.mypy_cache/,.ruff_cache/,.tox/__pycache__/,*.py[cod],*$py.class*.egg-info/,*.eggbuild/,dist/,.eggs/.vscode/,.idea/,.DS_Store
Test sequence:
- Clear + default re-index: 34 files / 663 chunks (correct — matches the 31
.py+ other source files that should be indexed, ~19 chunks per file because of substantial code content) index_codebase(force=true)run 1: 600 files / 600 chunks (incorrect — file count = chunk count = 1 chunk per file = trivial noise)index_codebase(force=true)run 2: 200 files / 200 chunks (different again, same 1:1 chunk-per-file ratio)index_codebase(force=true)run 3: 663 files / 663 chunks (different again)index_codebase(force=true)run 4: 400 files / 400 chunks (different again)- Clear + default re-index (sanity check): 34 files / 663 chunks (back to the correct count)
Expected behavior
force=true should re-index the codebase with the same file-selection logic as the default re-index path. The output (file count, chunk count, indexed content) should be consistent across runs and equivalent to clear_index + index_codebase without force=true.
Actual behavior
force=true produces random garbage results:
- File count varies wildly between runs (600, 200, 663, 400 on the same codebase)
- Chunks always equal files (1 chunk per file), indicating the indexed content is trivial/empty (vs the correct 19 chunks/file ratio on default indexing)
- The 1:1 chunk-per-file ratio strongly suggests force re-index is indexing junk directories that should be excluded (
.venv/,.pytest_cache/,__pycache__/,.egg-info/— files in these dirs are typically small/empty) .gitignoreexclusion logic does not appear to apply on the force re-index path
This makes the index unusable for semantic search — every force re-index produces different results, and search queries hit junk content instead of actual source code.
Root cause hypothesis
Looking at the source (per handlers.js in @zilliz/claude-context-mcp), force=true triggers a different code path that:
- Clears the Milvus collection during validation
- Calls
indexCodebase()withfalseas the third arg (likely aclearBeforeIndexor similar flag) - Does NOT use the same
getEffectiveIgnorePatterns()/.gitignoreloading path as the default re-index
The default re-index path correctly loads .gitignore patterns via getEffectiveIgnorePatterns(). The force re-index path appears to skip this loading, defaulting to broader file selection that pulls in .venv/, .pytest_cache/, and other ignored dirs.
Suggested fix
Either:
- Make
force=trueuse the samegetEffectiveIgnorePatterns()path as default re-index - Or document that the "correct" way to force re-index is
clear_index+index_codebase(force=false)— and either rejectforce=trueoutright or fix it to behave the same way
The current behavior is misleading — force=true returns success and a non-zero file count, but the indexed content is wrong.
Environment
@zilliz/claude-context-mcpv0.1.15 (latest as of 2026-07-28)- Vector DB: Milvus 2.x (self-hosted via Docker,
localhost:19530) - Embedding provider: OpenAI-compatible API via OpenRouter (
qwen/qwen3-embedding-8b, batch size 100) - Splitter: AST (
splitter: 'ast') - Discovered while debugging code-context MCP indexing for the OpenClaw codebase at
/workspace/projects/openclaw/
Related observations
I noticed two related but distinct issues while debugging this:
get_indexing_statusreports "completed" before the index has actually reached full coverage — on the OpenClaw codebase (29,456 git-tracked files), the default re-index path returned "fully indexed, 700 files" but continued to grow over time without any explicit re-index call. After ~10 minutes of waiting, the count had reached 3,000+ files and was still climbing. The MCP server appears to be doing background incremental indexing after marking the index "completed".The misleading "Codebase is already indexed. Use force=true to re-index." rejection message — when calling
index_codebasewithoutforce=trueon an already-indexed codebase, the tool rejects with this message, but the underlying behavior is that the background incremental indexer continues to scan and index new files. The rejection message is misleading and causes the calling agent (or human) to incorrectly conclude that incremental indexing is not happening.
Both of these are probably covered by #395 and #408 (or related) — but the force re-index garbage result bug I am reporting here is distinct from those.
Source: zilliztech/claude-context