fix(extraction): preserve receivers for method values (#1820) - #2034
Merged
Merged
Conversation
pool.submit(obj.method), thread_pool_exec(self.store.fetch), and Go Submit(c.store.Fetch) produced no callers/impact edge because only obj.method(...) call expressions were captured. Member values are now emitted as *.method function-refs and resolve unique-or-drop (same-file first). Two methods of the same name still produce no edge. Go go c.store.Fetch(ids) remains a calls edge (it is a call expression). EXTRACTION_VERSION 27: MCP daemon catch-up rebuilds a stale index on start so the watcher does not keep serving the hole after upgrade.
A unique production method plus a unit-test double of the same name was treated as ambiguous, so retrieve_vision_vectors still looked callback-invisible on a real repo. Prefer the non-test file when exactly one production target remains.
#1820 unique-or-drop treated DocStoreConnection.delete_payload_fields (the ABC stub) and QdrantConnection.delete_payload_fields as ambiguous, so gc_stale_member_stamps stayed invisible. Skip short not-implemented stubs when exactly one real implementation remains.
Python attributes and Go selectors used as callback values were dropped or reduced to bare method names. Preserve receiver paths in native and WASM extraction, resolve through type/import scope, and reject ambiguous or noncallable targets. Cover callback, assignment, collection, and query behavior, with extraction version 27 signaling the required reindex. Co-authored-by: Josh66 <info@aska-systems.de> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 27, 2026
colbymchenry
added a commit
that referenced
this pull request
Sep 27, 2026
Bisect identifies 5eaa6fe (#2034, #1820) as the first bad commit: its Python named-import fallback resolves task.delay() to the task function itself, hiding the queue effect in API steps. Require member resolution before returning a named Python import's target for attribute access. Keep direct calls, callbacks, constructors, and known members working. Add absolute/relative aliased-import regression coverage without changing the existing steps assertions. Validation: git bisect with preserved steps test, tsc and copy-assets at every step, CODEGRAPH_KERNEL=0 throughout bisect. 338 tests pass across ui-steps-api-servers, function-ref, rebuilt kernel-tsjs-parity, resolution, and ui-effects. Issue #1820 repro and tsc --noEmit pass.
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 27, 2026
…xing The Verilog commit's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the Verilog/SystemVerilog nodes and edges. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 27, 2026
This branch's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the record classes, component fields and accessors. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 27, 2026
Rebasing onto colbymchenry#2034 (method values keep their receiver): its Go branch passed inferLocalReceiverType's result straight to resolveMethodOnType, but that result now carries the declaring package directory (`<dir>\0<Type>`) or the external-type marker. `Submit(s.Fetch)` on a typed `s *Store` then looked up a type literally named `<dir>\0Store` and lost its edge. Decode it the way matchMethodCall does: an external type yields no edge, a project type resolves within its own package directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 27, 2026
…xing The Verilog commit's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the Verilog/SystemVerilog nodes and edges. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bompus
added a commit
to bompus/codegraph
that referenced
this pull request
Sep 28, 2026
The fork keeps its kernel-only extraction and resolution, its synthesis refresh on incremental sync, and its migrations v10-16. Upstream's TS resolver files (name-matcher, js-builtins, cpp-constructor, cpp-macro-visibility, synthesis-stage) and the Scala wasm grammar stay deleted. The kernel walkers carry the ported upstream kernel changes: NodeIdAllocator column suffixes, Python body docstrings, Scala objects as modules, the C++ constructor and macro-name helpers, and the TS/JS expression-receiver and curried-wrapper cases. Taken from upstream: - read-only CodeGraph.open/openSync (colbymchenry#1963), the shared project lease for MCP engines, and the query-pool and daemon fixes - sync throws LockUnavailableError when the file lock is held (colbymchenry#2014); open() destroys the instance if its sync fails - watcher lock-contention re-arm, explore miss diagnostics, the Scala grammar bump (colbymchenry#1823), and the vitest run-TMPDIR setup (colbymchenry#1879) - pending-reference recovery counts on SyncResult and in `codegraph sync` output (colbymchenry#1360), ported onto the fork's orphan sweep - `this.#field.method()` call edges (colbymchenry#1987), ported into the kernel's field-type matcher and method-call receiver pattern Fork-side adjustments: - edges keep the fork's `ORDER BY kind, id`. Upstream's `kind, target, line, col` tie-break (and a position-first variant) dropped CG-36's answer cluster and colbymchenry#1711's named gaps; the db-perf ordering test now checks kind precedence and insertion-order ties - a gap marker that can't afford its full name list names fewer symbols plus `+N more` instead of going bare (colbymchenry#2057 budgeting kept) - the lease catch-up sync defers synthesis, as the fork's engine did - enableWatcherAfterHandover nudges the existing lease instead of acquiring a second one - dropped idx_edges_synthesis_site from the bulk-load index list (the fork schema has no such index) - Python and Go function refs stay bare-named in both the kernel and the generic extractor until colbymchenry#1820 is ported to both - tests: the colbymchenry#1963 daemon test waits for attach before connecting; the Windows zip-archive cases run only where `zip` exists; the watcher and generated-flag tests keep their fork fixtures - golden dumps re-baselined; torture-multilang now covers 42 files Port follow-ups (tests skipped with a "kernel port pending" note): - colbymchenry#1820 receiver-path function refs (Python, Go) - colbymchenry#1838/colbymchenry#1839/colbymchenry#2035 C++ macro names and constructor calls - colbymchenry#1932/colbymchenry#2034 alias-binding object member boundaries - Scala companion resolution; colbymchenry#1857 Go bare calls; colbymchenry#1986 ABI evidence; expression receivers; HAR excluded imports; value+interface pairs - colbymchenry#2033 synthesis_inputs convergence model (7 tests) - colbymchenry#2043 stale-refusal banner policy needs a decision
This was referenced Sep 28, 2026
colbymchenry
added a commit
that referenced
this pull request
Sep 28, 2026
…e stall, per-ref rescans) — graph byte-identical (#2072) * perf(resolution): read JS/TS parameter lists without exponential backtracking isLocallyBoundJsName (#1759, cd4e65b) scans a file for a parameter that binds a bare-called name. Its per-parameter pattern `(type)?(default)?\s*` let one `name: T = value` text split several ways, so when the name was absent the search backtracked through every combination. On vscode's markersModel.test.ts, whose helper takes nine typed, defaulted parameters, each `suite`/`test` scan took 30-40s — one pool worker held a whole resolution batch while the rest idled, a 70-400s stall per run. Each parameter now has exactly one parse: its first non-space character after the identifier picks the type, default or bare alternative. The alternatives match exactly the strings the old optional groups did, so the result is identical for every input (4M generated cases and 5.6k real vscode file/name pairs agree); the scan is 0.1-0.3ms on that file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): memoize Python module-file lookups per resolution pass #2034 (5eaa6fe) sent every Python named import whose path the import resolver cannot place — stdlib and third-party modules such as `from unittest import mock` — through findPythonModuleFile, which scans every `__init__.py` file node for a path suffix. It ran once per ref: on django that scan alone went from 0.6s to 1.4s of resolution CPU. The candidates for a module depend only on the module path, so they are now collected once per resolution context, in the same name-lookup order; the importing file is still excluded per call, so the first survivor is the node the scan returned. The memo drops with the other import-resolver memos. Resolution output on django is hash-identical; single-threaded resolution CPU there goes from 6.5-6.8s to 4.9s (v1.6.0: 4.5s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): test sealed-module candidates cheapest-first isSealedModule (#1719, bffd50e) masks a candidate file's comments and strings before asking whether it is a module that exports nothing. Import refs call it for every same-named candidate across the tree, so on vscode it masked nearly every TypeScript file once per resolver: 3.3s of masking in a single-threaded pass, paid again in each pool worker. The verdict is a pure conjunction, so it now asks the cheap questions first: the source contains `import`, no node in the file is exported, and there is no CommonJS export. Masking runs only for files that pass all three, which in practice are the few that can be sealed. The masker blanks text and never deletes it, so a file without `import` in its source has none in its masked code either. Resolution output is hash-identical on vscode and django. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): scan a class for a field's declared type once per field matchTsThisFieldCall (#1496, cece072) resolves `this.<field>.<method>()` by finding the field's declaration in the enclosing class. It re-split the whole file, stripped comments line by line and compiled three patterns for every such call, although the answer depends only on the class and the field: 16.5s of a 148s single-threaded resolution pass on vscode. The first declaring line — `: typeof X`, `: X` or `= new X`, in that order per line — is now memoized per (class node, field), and the last few classes' stripped lines are kept, since calls arrive file by file. Owners are still tried in the same order and everything after the declaration lookup is unchanged. Resolution output is hash-identical on vscode and django. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(synthesis): start cross-tier receiver chains only at a word's start The cross-tier pass (b1f40c5) scans every non-test JS/TS file whose text has a `.add(`, `.get(` or `.on(` — nearly all of vscode — with receiver- chain patterns such as `a.b.c.add('job')`. A chain could begin at any letter of an identifier, so each letter re-read the dotted chain behind it; on vscode the pass ran 35s on one pool worker and set the synthesis phase's wall. A match that starts mid-word implies one from the word's first letter, and every match of these patterns ends on a non-word character, so a scan never resumes mid-word: a lookbehind that rejects those starts skips only attempts bound to fail. Over all 14,135 vscode JS/TS files the five patterns return identical matches (positions and groups) before and after, in 8.8s instead of 32.8s; the pass itself drops from ~9.8s to ~5.6s CPU standalone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): filter same-named candidates in one pass, cheap checks first Interface members (#1638) and nested const-bound functions (#1669) made common names — `editor`, `model`, `dispose` — carry thousands of nodes. matchByExactName ran ten chained filters over that list for every reference, copying it once per rule before the source-reading checks ran, and the method-call strategies copied it again to put the call site's file first before looking at only the class-like or same-file entries. Each rule judges one candidate on its own, so the exact-name rules now run as a single pass with the kind and language checks ahead of the lexical, sealed-module and local-binding checks; the class strategies narrow to owner kinds before ordering by file (the same nodes in the same order), and the object-literal strategy, which keeps same-file holders only, skips the reordering altogether. Resolution output is hash-identical on vscode and django. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(synthesis): scope language-specific node scans in SQL Eleven synthesis passes stream every node of a kind and keep only their language's (Go, Java/Kotlin, XML, native, ArkTS, Nix, Erlang). On a TypeScript monorepo the Go passes materialized all 113k vscode methods to find two Go ones — goMethodContains alone went from 0.5s to ~2.9s on the main thread after interface members (#1638) grew the method count and the canonical ORDER BY (#1988) landed. iterateNodesByKindIn adds the language predicate to the same query. Its ORDER BY (file_path, start_line, id) is a total order, so it yields exactly the nodes the callers kept, in the same sequence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(synthesis): run the synthesis-site index and source-gate scan beside the passes Two serial main-thread steps landed with the incremental synthesis refresh (#1988, #2038). The bulk-edge window rebuilt idx_edges_synthesis_site before synthesis — its partial predicate runs json_valid over the metadata of every edge (~1.5M rows, 2.7s on vscode) — and after the merged insert synthesis re-read every file in the project to record its source gates (~1.9s). Neither result is read until the index is finished: only sync consults the index, and the gates depend only on the files. The window's end now leaves that one index for later, and the resolver builds it while the pool computes the passes, when the main thread was only waiting; the source-gate scan runs in the same window. Both still happen before resolution returns, whatever path synthesis takes, and a crash in between is healed on the next open as before. On vscode edge-index-recreate drops from 8.3s to 3.6s of serial time; the graph, synthesis_inputs and the final index set are identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): stop re-reading resolved targets and re-probing missing files Every resolved reference now re-reads its target node several times — the kind gate (#1796), the cross-family language gate (#1986), alias following (#1482) and edge creation — through the query layer's 1,000-entry node cache, while targets recur ~5x (1.46M vscode edges onto 293k nodes). The language gate alone spent ~6s of single-threaded resolution on vscode fetching whole nodes to read one field, and fileExists re-probed the same missing candidate paths (every extension of a `.js` specifier) from every importing file, now under a path.resolve containment check (#1631). The resolver keeps its own id → node cache and a memo of filesystem probes, both in the stable window of its other caches and dropped by clearCaches; the language gate returns early when the reference's language has no code family (no target can cross one) and otherwise memoizes each target's language. Resolution output is hash-identical on vscode and django. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): cheaper sealed-module and receiver-less call checks isSealedModule read and decoded a candidate file — its source and every node — to learn whether anything is exported, though nearly every module does. It now asks one indexed EXISTS probe first and reads the source only for files that export nothing (the conjunction is unchanged). isReceiverLessCall (#1759, #1857) compiled a pattern per call and end-anchored two more over the line's prefix, which V8 scans from the start. It now matches the name literally with a sticky `\s*[(<]`, and reads the prefix backwards: the last non-space character decides whether a receiver precedes the call, and the word run ending there decides the keyword exception — exactly what `[.\w$\])]\s*$` and `\b(?:return|...)\s*$` accepted. Resolution output is hash-identical on vscode and django. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(synthesis): pull passes onto workers heaviest-first; cheaper registry and tier scans The pooled fan-out handed every pass out at once, so each worker held a count-based share and a heavy pass finished only with its worker's whole share (vscode's registry pass ended ~16s in). Workers now pull one pass at a time, heaviest first; edges still merge by registry index, so the output is unchanged. vscode synthesis: 14.5s vs 19.6-24.4s. The registry pass retried its greedy name at every character of every identifier; both scans now start a name only at an identifier's first character or a `this.` (a mid-identifier resume after a `.method` dispatch falls back to the unguarded pattern), and dispatch lines come from a newline-offset lookup instead of splitting the prefix per match. Checked match-for-match against the old patterns on every vscode JS/TS file plus 300k fuzzed strings. The tier pass reads a file's nodes only when a site needs them. Standalone on vscode: registry 5.6s -> 4.5s, tier 5.1s -> 4.4s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): lean export indexes; no per-name regex compiles in field and local-binding checks Three per-lookup costs the resolver pool paid once per worker: - A file's export index decoded every node in the file and read its source, for each file an import reached, in every worker. It now reads only the exported rows (same `start_line, id` order); the default-export binding and names a local `export { a as b }` clause introduces are read on first use, from name-targeted rows. findExportedSymbol across vscode's workers: 12.5s -> 3.7s. - The `this.<field>` declaration scan compiled three patterns per field of every class and walked every class line. Each pattern needs the field as a whole `[\w$#]` token, so a class's lines are indexed by token once and three sticky patterns, compiled once, are tried at the field's token occurrences. Identical on 721k vscode (file, field) pairs; 56.7s -> 1.1s standalone. - isLocallyBoundJsName searched the whole file with four patterns per name. Each can only start at a `const`/`let`/`var` or `function`/`class` keyword, an `=>`, or the `(` opening a list before the name, so the patterns (compiled once per name, sticky) are tried there. Identical on 198k vscode (file, name) pairs; 68.1s -> 10.7s standalone. Replaying every vscode (2.19M) and django (270k) reference single-threaded gives the same result hash before and after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): prepare kind-filtered edge reads once; memoize the inherited this-member walk - getOutgoingEdges / getIncomingEdges with a kind filter prepared a fresh statement on every call, a third of the read's cost; they are issued per node by supertype walks and the member-lookup synthesis passes. Each SQL shape is now prepared once per connection (dropped on rebind). - The post-loop `this.<member>` pass walked the same supertypes for every reference, reading each one's contains edges and decoding members one by one until a name matched. Nothing is written until the pass ends, so each type's supertype edges and each supertype's callable members (by name, in edge order) are read once. vscode: 4.1s -> 1.0s. - Express's mount pass decoded every JS/TS file to look for `.use(`; it now checks the raw bytes first (an ASCII needle survives UTF-8 decoding unchanged) and decodes only files that have it. A full vscode init dumps the identical graph before and after. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(resolution): fan the next batch out before inserting edges resolution never reads The batch loop inserted every batch's edges before fanning out the next one, so the pool sat idle through each insert. That ordering exists for one reason: later batches walk supertype chains over the `extends` / `implements` edges earlier batches resolved. Those are the only edges resolution reads, and the prerequisite phase drains every such ref first, so after it no batch makes one. A batch without them now fans the next batch out first and inserts while it resolves: the same writes in the same order, and nothing the workers read differs. The kinds live in one constant, shared with the supertype walk. vscode resolution, three interleaved rounds against the parent commit: 50.3-51.0s vs 56.8-59.1s wall (the pool's wait per batch halves; the overlapped inserts cost ~7s more CPU). Full vscode and django inits dump identical graphs before and after, and identical to main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(db): prepare the edge-endpoint existence check once Every edge insert checks its endpoints with a 500-id IN query, a chunk at a time, and prepared that statement afresh for each chunk. The batch loop now runs these inserts on the main thread while the pool resolves, so the full-size statement is prepared once; only a final partial chunk prepares ad hoc (as deleteReferencesByRowIds already does). Same SQL, same result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 29, 2026
…xing The Verilog commit's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the Verilog/SystemVerilog nodes and edges. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 30, 2026
…xing The Verilog commit's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the Verilog/SystemVerilog nodes and edges. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 30, 2026
Rebasing onto colbymchenry#2034 (method values keep their receiver): its Go branch passed inferLocalReceiverType's result straight to resolveMethodOnType, but that result now carries the declaring package directory (`<dir>\0<Type>`) or the external-type marker. `Submit(s.Fetch)` on a typed `s *Store` then looked up a type literally named `<dir>\0Store` and lost its edge. Decode it the way matchMethodCall does: an external type yields no edge, a project type resolves within its own package directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Sep 30, 2026
This branch's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the record classes, component fields and accessors. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Oct 1, 2026
…xing The Verilog commit's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the Verilog/SystemVerilog nodes and edges. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Oct 1, 2026
Rebasing onto colbymchenry#2034 (method values keep their receiver): its Go branch passed inferLocalReceiverType's result straight to resolveMethodOnType, but that result now carries the declaring package directory (`<dir>\0<Type>`) or the external-type marker. `Submit(s.Fetch)` on a typed `s *Store` then looked up a type literally named `<dir>\0Store` and lost its edge. Decode it the way matchMethodCall does: an external type yields no edge, a project type resolves within its own package directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Oct 3, 2026
Rebasing onto colbymchenry#2034 (method values keep their receiver): its Go branch passed inferLocalReceiverType's result straight to resolveMethodOnType, but that result now carries the declaring package directory (`<dir>\0<Type>`) or the external-type marker. `Submit(s.Fetch)` on a typed `s *Store` then looked up a type literally named `<dir>\0Store` and lost its edge. Decode it the way matchMethodCall does: an external type yields no edge, a project type resolves within its own package directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Oct 3, 2026
…xing The Verilog commit's bump to 27 now equals main, where 27 was already taken by colbymchenry#2034, so indexes built by main would not be asked to re-index for the Verilog/SystemVerilog nodes and edges. Move to the next number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Problem
Methods passed as values, such as
pool.submit(self.store.fetch, ids), were missing from callers and impact results. PR #1894 captured these values but erased receiver identity and could select unrelated methods.Fix
Build on the contributor commits while preserving Python and Go receiver paths in native/WASM extraction. Resolve through receiver, type, and import scope; reject ambiguity and noncallable values without excluding test files or inspecting bodies for NotImplementedError. Preserve references edges with fnRef metadata and retain extraction version 27. Existing projects should run
codegraph indexto backfill unchanged files.Validation
repro.sh "$PWD": failed before; passed after rebuilding with default routing andCODEGRAPH_KERNEL=0.npm run build:kernelnpx tsc --noEmitnpx tsc && npm run copy-assetsnpx vitest run __tests__/{function-ref,kernel-tsjs-parity,kernel-grammar-parity,kernel-swift-parity,resolution,call-receiver-no-fabrication,python-quoted-annotation,python-module-scope-collection-methods,upgrade,extraction}.test.ts --maxWorkers=2 --minWorkers=1: 1,027 passed.CODEGRAPH_KERNEL=0 npx vitest run __tests__/function-ref.test.ts --maxWorkers=2 --minWorkers=1: 29 passed.Fixes #1820
🤖 Generated with Claude Code