Repository navigation
Description
Version
v0.8.1-282-g1cca858 — built from source at commit 1cca858
Platform
macOS 15 (Darwin 24.6.0), arm64
Install channel
Built from source: make -f Makefile.cbm cbm
Binary variant
Standard (no UI). Built without libgit2 (pkg-config libgit2 absent, so HAVE_LIBGIT2 was not set) — I don't believe that's relevant to discovery/ignore handling, but flagging it in case.
What happened, and what did you expect?
A root-anchored .gitignore pattern is applied to nested paths, so index_repository silently excludes tracked source directories that git itself does not ignore.
Under gitignore semantics, a leading / anchors the pattern to the directory containing the .gitignore. So /coverage must match <root>/coverage and must not match <root>/src/routes/coverage. git check-ignore agrees. CBM excludes both.
The effect is silent: the excluded directory appears in excluded.dirs (good), but index_repository still returns status: "indexed", so a consumer can't distinguish "this directory has no symbols" from "this directory was dropped". Every symbol in it is simply absent from the graph.
I hit this on a real SvelteKit app where /coverage (the test-coverage output) is gitignored at the root, and a legitimate route directory happens to be named coverage. That route vanished from the graph with no error.
Reproduction
mkdir -p repro/coverage repro/src/routes/coverage && cd repro
git init -q .
printf '/coverage\n' > .gitignore # ROOT-anchored
printf 'def root_helper():\n return 1\n' > coverage/root_report.py # should be ignored
printf 'def nested_handler():\n return 2\n' > src/routes/coverage/page.py # should NOT be ignored
printf 'def app_main():\n return 3\n' > src/app.py
git add -A && git -c user.email=t@t -c user.name=t commit -qm initGit's verdict:
$ git check-ignore -q coverage/root_report.py && echo IGNORED
IGNORED
$ git check-ignore -q src/routes/coverage/page.py && echo IGNORED || echo "not ignored"
not ignored
$ git ls-files
.gitignore
src/app.py
src/routes/coverage/page.py # <- trackedCBM's verdict:
$ codebase-memory-mcp cli index_repository --repo-path . --persistence false
{"excluded":{"dirs":[".git","coverage","src/routes/coverage"], ...}, "nodes":18, ...}
^^^^^^^^^^^^^^^^^^^^^^ should not be hereResulting graph:
$ sqlite3 -readonly ~/.cache/codebase-memory-mcp/<project>.db \
"SELECT file_path FROM nodes WHERE label='File';"
src/app.py
$ sqlite3 -readonly ... "SELECT name, file_path FROM nodes WHERE label='Function';"
len|<python-builtins>
print|<python-builtins>
app_main|src/app.pynested_handler is gone. src/routes/coverage/page.py has no File node at all.
Expected: excluded.dirs contains .git and coverage only; src/routes/coverage/page.py is indexed and nested_handler appears as a Function.
Logs
CBM_LOG_LEVEL=info shows nothing beyond the normal pipeline lines — the exclusion isn't logged as a decision, which is part of why it's easy to miss.
Project scale (if relevant)
Trivial for the repro above. Originally found on a ~170-file SvelteKit repo.
One more thing, possibly separate
On that same real repo, 4 of 32 tracked .svelte files produced no nodes. Exactly one is explained by the bug above. The other three are not gitignored, not in excluded.dirs, and not size-related — the largest .svelte in the repo (114,675 bytes) indexes fine while an 8,657-byte file is dropped. Bracketed path segments aren't universally fatal either, since src/routes/[[tab=dashboardTab]]/+page.svelte indexes correctly.
I haven't isolated that one into a minimal repro yet, so I've left it out of the title. Happy to open it separately if you'd prefer — or it may share a root cause with the discovery path above.
Confirmations
- Searched existing issues; didn't find this one.
- Reproduction above is minimal and self-contained.
Activity
DeusData commentedon Jul 25, 2026
Thank you for this — the reproduction is excellent, and the bug is real and confirmed. I want to give you a correction on the root cause, though, because it turns out to be a bigger problem than the one you described, in a different subsystem.
The symptom is confirmed
src/routes/coverageis silently dropped while git tracks it. Reproduced on currentmain.But
.gitignoreanchoring is not the causeThe gitignore engine actually handles your
/coveragepattern correctly:gi_add_pattern()(src/discover/gitignore.c:211-215) setsrooted = trueand strips the leading/.glob_match(p->pattern, rel_path)(gitignore.c:362), andrel_pathis the full repo-relative path, built atsrc/discover/discover.c:837.glob_match()is anchored at both ends (gitignore.c:157-162), soglob_match("coverage", "src/routes/coverage")fails on the very first character.The decisive test — your repro with no
.gitignoreat all:Both directories are still excluded with no ignore file present anywhere. So
.gitignoreis not involved.The actual cause: the built-in skip list is basename-matched at any depth
ALWAYS_SKIP_DIRS(src/discover/discover.c:31-51) contains"coverage"on line 40, andcbm_should_skip_dir()(discover.c:339-345) takes only the basename —discover.c:570passesentry->name, never the relative path. So any directory namedcoverage, at any depth, is dropped before ignore rules are ever consulted.That list also contains
env,deploy,deployed,target,temp,tmp,dist,obj,vendor,vendored, andpublic-adjacent names — all plausible route or module names. Ten tracked, non-ignored sibling directories undersrc/routes/, in--mode full:Nine of ten silently dropped. Only
src/routes/normalsurvived. In non-FULLmodesFAST_SKIP_DIRS(discover.c:53+) addsdocs,scripts,tools,examples,static,assets,migrations,integrationand more on the same basename-at-any-depth rule — so the exposure is wider still outside full mode.Your SvelteKit case is the general form of this: a framework where route directories are named after ordinary English words collides with a skip list designed for repo-root build artifacts.
Your two secondary points
The silent
status: "indexed"— agreed that this is what makes it dangerous rather than merely annoying, and thatexcluded.dirsbeing technically-correct-but-unread is not much of a defence. How the status contract should change is a design decision I don't want to settle inside triage, so I'm recording it rather than answering it here.The 3 unexplained
.sveltefiles — not explained by this bug, and not by size given your 114 KB/8.6 KB counterexample. Please do open that separately; with the coverage directory now accounted for, the remaining three are a clean signal and I'd rather track them on their own than have them absorbed into this one.Thanks again — filing this with a full
git check-ignorecomparison and a minimal repro is what made the real cause findable in one pass.DeusData commentedon Oct 19, 2026
Thank you for the clarification and examples. The earlier triage corrected this to built-in basename exclusions, rather than root anchoring in .gitignore. I have no new change to that exclusion/status behavior to report.