Version: memtrace 0.8.63
Platform: Windows 11 Pro (10.0.26200), MemDB mode local
Runtime: single memtrace start (owner + watcher + MCP on :3030), watcher attached to the repo
Affected file language: JavaScript (ESM)
Summary
A top-level exported function was deleted from a file in a normal commit. The file still exists. Hours later, after the watcher had completed a full index and an embeddings pass, the graph still returns three Function nodes for that symbol, at three different line ranges, with found: true and no staleness signal. Two of the three ranges point past end-of-file.
The nodes appear to be snapshots of the symbol from three different points in the file's edit history. find_symbol is a present-time query (as_of defaults to "now"), so a bi-temporal invalid_at should exclude them. It does not appear to be set on deletion.
This is not indexing lag. It was re-measured after a complete index + embed cycle and was byte-identical — same three node IDs, same line ranges.
Impact
find_symbol confidently returns a function that does not exist, with found: true, is_exported: true, and a _pre_edit_check envelope reporting risk_level: low, direct_callers: 0. An agent instructed to prefer Memtrace over grep for code discovery will reason off a deleted symbol, and nothing in the response indicates the result is historical.
direct_callers: 0 is the most dangerous part — it reads as "safe, nothing depends on this," when the correct answer is "this does not exist."
Reproduction
- Have a watched repo with
memtrace start running.
- Pick a top-level exported function in a JS file that has been edited several times over its history, so the symbol has occupied different line numbers across commits.
- Delete only that function, leaving the file in place. Commit.
- Let the watcher index — confirm via
get_repository_stats that last_indexed is newer than the commit and that an embed_complete episode has landed.
find_symbol(name=<symbol>, repo_id=<repo>).
Expected: 0 matches, or matches flagged as historical.
Actual: one node per historical position of the symbol, all returned as current.
Observed case
Symbol getCurrentEngine in a JS module (private repo, paths generalized).
| Fact |
Value |
| Occurrences in pre-deletion blob |
1, at line 104 |
| Deletion commit |
1 file changed, 8 deletions(-) |
| Occurrences in HEAD blob |
0 |
| Current file length |
98 lines |
last_indexed after deletion |
newer than the deletion commit |
| Last episode |
embed_complete, anchored to a later commit |
indexing_incomplete |
false |
find_symbol returned 3 matches, all kind: Function, is_exported: true:
| node id |
start–end |
provenance.episode_uuid |
memdb:281475057616547 |
91–93 |
75b9c0e1-… |
memdb:281475045834150 |
103–105 |
null |
memdb:281475004340836 |
104–106 |
3894c05f-… |
Two observations:
- Ranges 103–105 and 104–106 exceed the file's 98 lines.
- The node with
episode_uuid: null reads as a HEAD-indexed node rather than a historical episode snapshot. If so, the re-index should have replaced or removed it. It did not. This is the one least explicable as expected bi-temporal history.
Why the existing cleanup primitives don't cover it
cleanup_stale_records(check_missing: true) matches on file_path missing from disk. The file still exists, so these records are never candidates. Confirmed by dry run — it matched 3 unrelated paths and would delete 127 nodes, none of them these.
cleanup_episodes documents that HEAD-indexed nodes are never deleted, putting the null-provenance node out of its reach by design.
- Plain re-index does not clear them (demonstrated above).
So there appears to be no documented primitive that removes a stale symbol node inside a surviving file, short of delete_repository + full re-index.
Secondary finding (separate, lower severity)
In the same repo, cleanup_stale_records(check_missing: true, dry_run: true) reported 127 orphan nodes across 3 file paths that no longer exist on disk, out of 17,948 records scanned (~5.2s). One of those files had been deleted in a commit roughly a day earlier. So orphans from deleted files also survive watcher-driven indexing until cleanup_stale_records is run manually — that case at least has a working remedy.
What I did not verify
Stated explicitly so nothing here is taken as more than it is:
- I did not confirm that the ~+4,780 total-node growth observed across one index cycle (16,415 → 21,195; Methods +259, Functions +59) is caused by duplicate accumulation. The commits in that window were mostly documentation, so the growth looked disproportionate, but I did not isolate it and it may have an ordinary explanation.
- I could not time or test
memtrace index . --fresh as a remedy. Run from the repo root while memtrace start was active, it emitted its first line and then made no progress until killed — consistent with contending for the MemDB the daemon owns, though I did not confirm that cause. If a fresh rebuild is the intended remedy, it may be worth documenting that the runtime must be stopped first.
- I did not test whether this reproduces on non-Windows platforms, in other languages, or on a symbol that has only ever occupied one line range.
Version: memtrace 0.8.63
Platform: Windows 11 Pro (10.0.26200), MemDB mode
localRuntime: single
memtrace start(owner + watcher + MCP on :3030), watcher attached to the repoAffected file language: JavaScript (ESM)
Summary
A top-level exported function was deleted from a file in a normal commit. The file still exists. Hours later, after the watcher had completed a full index and an embeddings pass, the graph still returns three
Functionnodes for that symbol, at three different line ranges, withfound: trueand no staleness signal. Two of the three ranges point past end-of-file.The nodes appear to be snapshots of the symbol from three different points in the file's edit history.
find_symbolis a present-time query (as_ofdefaults to "now"), so a bi-temporalinvalid_atshould exclude them. It does not appear to be set on deletion.This is not indexing lag. It was re-measured after a complete index + embed cycle and was byte-identical — same three node IDs, same line ranges.
Impact
find_symbolconfidently returns a function that does not exist, withfound: true,is_exported: true, and a_pre_edit_checkenvelope reportingrisk_level: low,direct_callers: 0. An agent instructed to prefer Memtrace over grep for code discovery will reason off a deleted symbol, and nothing in the response indicates the result is historical.direct_callers: 0is the most dangerous part — it reads as "safe, nothing depends on this," when the correct answer is "this does not exist."Reproduction
memtrace startrunning.get_repository_statsthatlast_indexedis newer than the commit and that anembed_completeepisode has landed.find_symbol(name=<symbol>, repo_id=<repo>).Expected: 0 matches, or matches flagged as historical.
Actual: one node per historical position of the symbol, all returned as current.
Observed case
Symbol
getCurrentEnginein a JS module (private repo, paths generalized).1 file changed, 8 deletions(-)last_indexedafter deletionembed_complete, anchored to a later commitindexing_incompletefalsefind_symbolreturned 3 matches, allkind: Function,is_exported: true:provenance.episode_uuidmemdb:28147505761654775b9c0e1-…memdb:281475045834150nullmemdb:2814750043408363894c05f-…Two observations:
episode_uuid: nullreads as a HEAD-indexed node rather than a historical episode snapshot. If so, the re-index should have replaced or removed it. It did not. This is the one least explicable as expected bi-temporal history.Why the existing cleanup primitives don't cover it
cleanup_stale_records(check_missing: true)matches onfile_pathmissing from disk. The file still exists, so these records are never candidates. Confirmed by dry run — it matched 3 unrelated paths and would delete 127 nodes, none of them these.cleanup_episodesdocuments that HEAD-indexed nodes are never deleted, putting thenull-provenance node out of its reach by design.So there appears to be no documented primitive that removes a stale symbol node inside a surviving file, short of
delete_repository+ full re-index.Secondary finding (separate, lower severity)
In the same repo,
cleanup_stale_records(check_missing: true, dry_run: true)reported 127 orphan nodes across 3 file paths that no longer exist on disk, out of 17,948 records scanned (~5.2s). One of those files had been deleted in a commit roughly a day earlier. So orphans from deleted files also survive watcher-driven indexing untilcleanup_stale_recordsis run manually — that case at least has a working remedy.What I did not verify
Stated explicitly so nothing here is taken as more than it is:
memtrace index . --freshas a remedy. Run from the repo root whilememtrace startwas active, it emitted its first line and then made no progress until killed — consistent with contending for the MemDB the daemon owns, though I did not confirm that cause. If a fresh rebuild is the intended remedy, it may be worth documenting that the runtime must be stopped first.