Repository navigation
fix(resolution): what a resolver keeps goes when what it was read from moves - #3214
Merged
Merged
Conversation
…m moves A resolver lives as long as its CodeGraph, so a server syncs on one for days. Eight memos in name-matcher and three in frameworks outlived clearCaches, and the Cargo crate map and compile database's include directories were read once for the process, so an edit was resolved by what the file said before it. The eight go in clearNameMatcherMemos, and the framework memos renew with the node lists, whose identity changes with the caches. Cargo manifests and compile databases join the module-config snapshot: a sync stats them, reads one only when its stamp moved, and re-opens Rust or C and C++ imports when what resolution reads from it did. A crate under a glob member is found when files come or go. importMappingCache and the two clear functions no one called go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es no rejection Each worker's ready promise rejects when a start fails, and only ready() handled that. Terminating a worker that hasn't said ready exits it with code 1, so a pool torn down mid-boot left an unhandled rejection when the timing gave it that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bompus
added a commit
to bompus/codegraph
that referenced
this pull request
Nov 2, 2026
…m moves (colbymchenry#3214) Ported from upstream f72c9d2. Framework memos (Angular workspace roots, CICS transaction ids, Expo native bindings) renew with the node list, so a sync reads the edited tree. Cargo manifests and compile databases join the module-config snapshot: a sync stats them, reads one only when its stamp moved, and re-opens Rust or C and C++ imports when what resolution reads from it did; the Cargo crate map is no longer cached per context in the Rust framework. The kernel's own per-run memos go with the resolver, which a sync closes, so no name-matcher memo set is ported. CHANGELOG bullets for this batch's behaviour changes are included.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A resolver lives as long as its CodeGraph, so
codegraph serveand a watcher sync on one object for days. Thirteen things it remembered survivedclearCaches, so a sync resolved an edit by what the file said before it. The wrong answer goes into the index, where a restart doesn't undo it until that file is edited again.Nothing changes for a one-shot
codegraph initorsync: full indexes are byte-identical to main.Root cause
clearCachesdrops whatclearNameMatcherMemosand its peers name. Eight memos inname-matcher.tsweren't named, and three framework memos have no clear function at all. The Cargo crate map and the compile database's include directories were read once and kept for good, the second in a process-level map whose clear functions nothing called.GO_BUILD_CONSTRAINTS//go:buildlinessep_a.gosep_b.goGO_BUILD_TWINStarget.idsep_a.gosep_b.goSFC_SCRIPT_RANGES<script setup>to<script>run -> helperOBJC_MEMBER_TABLES@property Foo *toBaz *Foo::runBaz::runOBJC_CLASS_FILESWidget::refreshGadget::refreshRECEIVER_ONLY_NAMESf()tof(history)History::backCPP_SPECIALIZATION_ARGUMENTSn.idread<int>read<long>CPP_SPECIALIZATION_OWNER_ARGUMENTSn.idformatter::formattransidIndexesONETWObindingMemorequireNativeModule('ExpoTwo')ExpoOne.pingExpoTwo.pingworkspaceRootsnx.jsonappearsnavigates /lyricscargoWorkspaceMapCachecppIncludeDirCachecompile_commands.jsonwith-Ithirdother/foo.hthird/foo.hThe fix
clearNameMatcherMemos.clearCachesshouldn't reach into one.<digest>@<mtime>:<size>. A sync stats each file and reads one only when its stamp moved, for every config file, tsconfig and go.mod included. A file whose digest is the same re-opens nothing, so a version bump or a build that rewrites the same database costs one read.crates/*names no file the snapshot holds, so a sync that added or removed files reads the workspace again. The include directories found by listing top folders, used with no compile database, are read again at the same point.importMappingCacheand the two clear functions with no callers are gone.clearCppIncludeDirCachewas called byresolution.test.tsalone.name-matcher-memo-clears.test.tsfails on a module-level memo inname-matcher.tsthatclearNameMatcherMemosdoesn't drop, with a six-name allow-list, each with its reason.Not covered: a path dependency outside the workspace (
c = { path = "../c" }) isn't in the snapshot, so a rename in its manifest waits for an edit to whoever imports it. The manifests of a project's own crates are watched.Validation
Windows 11.
Red before green. 16 tests red on main, one per item plus the Cargo and compile database shapes, in
sync-memo-lifetime.test.tsandsync-module-config-change.test.ts. Each indexes, edits and syncs on one object and compares edges and unresolved references with a fresh index. 13 mutations of the fix all turn a test red.The six found by reading are all real, each with a fixture red on main.
A/B against main, every dump compared byte for byte. Each repo twice: a full index, and an index, a comment added to one file and a sync on the same object. No edge moved, as expected: a fresh process has nothing stale, and a comment gives a memo nothing to go stale on.
Cost. 8 interleaved rounds against main, medians, wall s and CPU s:
Rounds on this box spread by 10% or more, so 1% can't be shown by timing alone. rocksdb's sync after an edit reads +7% above, so I profiled it: 4 interleaved runs, profile self time 31.2 s on main and 31.1 s here, walls 7.59 to 8.39 and 7.75 to 8.12, and no function in the top 16 moved. A sync adds 6 stats for the new candidates and no longer reads a config file that hasn't moved.
Suite on this head, with a kernel built from it: 880 files passed, 6 skipped; 11,042 passed, 2 expected fail, 87 skipped, 0 failed; 4 workers, Vitest 4.1.11.
Also here
A second commit fixes a fault of mine from #3192, which a full suite here turned up: 1 unhandled rejection,
resolver worker exited with code 1, inworker-teardown.test.ts. Each worker's ready promise rejects when a start fails, and onlyready()handled that. Terminating a worker that hasn't said ready exits it with code 1, so a pool torn down mid-boot left a rejection nobody handled, when the timing gave it that. The new test in that file is red without the line. It isn't in the CHANGELOG, since #3192 hasn't shipped.🤖 Generated with Claude Code