Skip to content

bug: index_health reports 100% while the graph holds orphan nodes, a deleted-repo ghost, and ~30% purgeable edges #311

Description

@tiendungdev

Environment

  • gortex v0.60.0+dd3bb30, Windows 11 Pro (10.0.26200)
  • 1 tracked repo, ~2,581 files (1,070 Go / 635 TS / 287 SQL), MCP + CLI

Summary

index_health answered health=100.0% stale=0 failures=0 while the graph
simultaneously contained three classes of garbage. A full rebuild
(untrack + track --wait) removed 44,686 nodes (−14.7%) and
548,432 edges (−29.6%):

Metric Before rebuild After rebuild
nodes 304,249 259,563
edges 1,850,822 1,302,390

None of this was visible in index_health, daemon status, or any warning.

What the garbage was (all verified against git grep / filesystem)

  1. Orphan symbols from a deleted directory. tmp/agent-skills/ (a copy of
    the source tree) was deleted from disk; its ~8,886 symbols stayed in the
    graph and kept appearing in search_symbols results and the generated
    community skills table.
  2. A ghost repo. A second tracked repo's directory was deleted from disk;
    gortex repos still listed it (HEAD (none), stale) days later. Nothing
    flagged it (see bug: daemon status and gortex repos disagree about tracked repos; a repo whose directory was deleted is never flagged #312).
  3. Stale line numbers served as truth. search_symbols returned a symbol
    at line 258; the file's real line was 388. git log showed line 258 was
    correct 12 days earlier — a frozen snapshot, indistinguishable from
    live data in the response.

Why health misses it

Health appears to compare indexed HEAD vs current HEAD per repo. It never
checks whether node absolute_file_paths still exist, so nodes owned by
deleted files/directories/repos are invisible to it. store_sqlite already
has OrphanRepoPrefixes(known) for prefix-level orphans, but nothing covers
path-level orphans inside a still-tracked repo.

Proposed fix

  1. Add a path-liveness sample to index_health: sample N nodes per repo,
    stat their absolute_file_path, report an orphan_rate and drop the
    health score when it is non-zero. Full scan could be a --deep flag.
  2. On re-warm, tombstone nodes whose file no longer exists (cheap: the watcher
    already knows deletions it witnessed; this covers deletions it missed).
  3. Add a gortex reindex [repo] verb = atomic untrack+track --wait with
    orphan purge. The full rebuild that fixed all of the above took only
    4m28s for 2,581 files, so this is a cheap escape hatch users cannot
    currently discover (today the only path is manual untrack/track).
    Related CLI-lifecycle ask: Add gortex uninstall and gortex upgrade commands (especially for Windows) #298.

Confirmed repro (isolated daemon, v0.60.0, fresh home)

1. add repo-a/tmpcopy/gamma.go (unique symbol)   -> search total: 1
2. rm -rf repo-a/tmpcopy; wait for the watcher   -> search total: 2   (!!)
3. index_health                                  -> health=100.0% nodes=16 stale=0 failures=0

Deleting the file did not remove the symbol — it doubled it (the deleted
file's node now exists under both the prefixed and unprefixed identity; see
#309), and health stayed 100%. The production numbers at the top
are this same mechanism compounded over weeks.

Repro sketch

# 1. track a repo, let it warm
# 2. copy a subtree: cp -r src tmp/copy && wait for index
# 3. rm -rf tmp/copy
# 4. gortex call index_health   -> still 100%, no orphan signal
# 5. search_symbols for a symbol unique to tmp/copy -> still returned

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions