Summary
When a function is re-exported under a value alias (export const alias = fn), CodeGraph indexes the call edge to the alias and the binding alias = fn, but never composes them. callers and impact on the implementation therefore omit every consumer that calls it through the alias — and report a complete-looking answer with a precise count and no uncertainty marker.
Verified on a 2-file, 6-node repro (below) and reproduced in a 16k-file TypeScript monorepo, where impact on a real function omitted both of its production call sites and listed only the definition file and its tests.
Minimal reproduction
Two files, fresh repo, nothing else:
impl.ts
export function realImpl(): string {
return "real";
}
export const aliasName = realImpl;
consumer.ts
import { aliasName } from "./impl.js";
export function consumerFn(): string {
return aliasName();
}
git init . && git add -A && git commit -m repro
codegraph init . # 6 nodes, 8 edges
Actual
$ codegraph callers realImpl
Callers of "realImpl" (1):
file impl.ts
impl.ts:1
$ codegraph impact realImpl
Impact of changing "realImpl" — 2 affected symbols:
impl.ts
function realImpl:1
file impl.ts:1
consumer.ts does not appear in either. Changing realImpl breaks consumerFn, and impact analysis reports the blast radius as the definition file alone.
The control query shows the missing half of the join is present in the graph:
$ codegraph callers aliasName
Callers of "aliasName" (2):
function consumerFn
consumer.ts:3
file consumer.ts
consumer.ts:1
So the index contains consumerFn → aliasName and aliasName = realImpl. Only the composition is missing.
Expected
callers realImpl and impact realImpl should include consumerFn / consumer.ts, by resolving value aliases whose initializer is a direct reference to an indexed function. Failing that, the output should mark the result as potentially incomplete rather than presenting a count.
Why this is more than a missing edge
The failure is a false negative shaped like a complete answer. There is no "exact match not found" or "possible missing edges" signal, so a consumer cannot distinguish "nothing depends on this" from "the traversal stopped early".
That interacts badly with the MCP steering text. codegraph_explore's description instructs the agent to treat the returned source as already read and not re-open those files (src/mcp/tools.ts:682, src/mcp/server-instructions.ts:32,55), and no environment variable disables it. An agent asking "what breaks if I change this?" is told the blast radius is the definition file, and told not to run the verification that would catch the omission. For impact, which exists to answer exactly that question, silence is indistinguishable from safety.
Related
Same family as the per-language traversal gaps in #1187 (Java field injection), #1373 (C/C++ macros), and #899 (Python module attributes), but for TypeScript value-alias re-export. I could not find an existing report for this pattern.
Environment
- CodeGraph 1.5.0,
codegraph-darwin-arm64.tar.gz release bundle (bundled Node v24.16.0)
- macOS arm64
DO_NOT_TRACK=1, CODEGRAPH_NO_UPDATE_CHECK=1
- Also reproduced through
serve --mcp with CODEGRAPH_CATCHUP_GATE_TIMEOUT_MS=0 (fully reconciled index, so not a staleness artifact)
Summary
When a function is re-exported under a value alias (
export const alias = fn), CodeGraph indexes the call edge to the alias and the bindingalias = fn, but never composes them.callersandimpacton the implementation therefore omit every consumer that calls it through the alias — and report a complete-looking answer with a precise count and no uncertainty marker.Verified on a 2-file, 6-node repro (below) and reproduced in a 16k-file TypeScript monorepo, where
impacton a real function omitted both of its production call sites and listed only the definition file and its tests.Minimal reproduction
Two files, fresh repo, nothing else:
impl.tsconsumer.tsActual
consumer.tsdoes not appear in either. ChangingrealImplbreaksconsumerFn, and impact analysis reports the blast radius as the definition file alone.The control query shows the missing half of the join is present in the graph:
So the index contains
consumerFn → aliasNameandaliasName = realImpl. Only the composition is missing.Expected
callers realImplandimpact realImplshould includeconsumerFn/consumer.ts, by resolving value aliases whose initializer is a direct reference to an indexed function. Failing that, the output should mark the result as potentially incomplete rather than presenting a count.Why this is more than a missing edge
The failure is a false negative shaped like a complete answer. There is no "exact match not found" or "possible missing edges" signal, so a consumer cannot distinguish "nothing depends on this" from "the traversal stopped early".
That interacts badly with the MCP steering text.
codegraph_explore's description instructs the agent to treat the returned source as already read and not re-open those files (src/mcp/tools.ts:682,src/mcp/server-instructions.ts:32,55), and no environment variable disables it. An agent asking "what breaks if I change this?" is told the blast radius is the definition file, and told not to run the verification that would catch the omission. Forimpact, which exists to answer exactly that question, silence is indistinguishable from safety.Related
Same family as the per-language traversal gaps in #1187 (Java field injection), #1373 (C/C++ macros), and #899 (Python module attributes), but for TypeScript value-alias re-export. I could not find an existing report for this pattern.
Environment
codegraph-darwin-arm64.tar.gzrelease bundle (bundled Node v24.16.0)DO_NOT_TRACK=1,CODEGRAPH_NO_UPDATE_CHECK=1serve --mcpwithCODEGRAPH_CATCHUP_GATE_TIMEOUT_MS=0(fully reconciled index, so not a staleness artifact)