Repository navigation
Description
Status: independently reproduced 2026-11-08 on em.40, pristine upstream main and upstream v1.6.2.
This is separate from the external-import/missing-dependency issue tracked in
#26. No resolver fix is included.
Minimal reproduction
// main.js
const utils = require('./utils');
function run() { return utils.missing(); }
module.exports = run;// utils.js — a real local module, but it does not export missing
exports.known = function known() { return 1; };// decoy.js — nothing imports or instantiates Decoy
export class Decoy { missing() { return 42; } }From the directory containing these three files, set BUILD to the tested codegraph.js and run:
CODEGRAPH_KERNEL=0 node "$BUILD" init -y
CODEGRAPH_KERNEL=0 node "$BUILD" callees run -jFresh indexing correctly records main.js importing utils.js. It then fabricates:
run, main.js:2 --calls--> decoy.js:Decoy::missing
refName: utils.missing
resolvedBy: instance-method
provenance: null
Expected: missing stays unresolved because utils.js exports no such member. The decoy is neither
imported nor a runtime receiver of the call. Without decoy.js the reference is unresolved.
The extractor retains utils.missing, so the receiver is not lost at extraction.
This is an asserted wrong call edge, not merely an edge labelled as a heuristic. The graph now
attributes a caller to a method the source never invokes. No downstream impact-query output was
separately measured.
Independent verification
All subjects used Node22.22.2 on Linux/WSL and kernel0/WASM.
| Subject | Commit | Semantic outcomes |
|---|---|---|
| Deployed em.40 | 5e06648 | 5 controls pass, missing-method counterexample fails |
| Official upstream main | f72c9d2 | Same six case/edge records as em.40 |
| Official upstream v1.6.2 tag | 6560052 | Missing-method counterexample also fails; real-export control also fails |
The already independently built upstream subjects were reused: official GitHub clones, fresh
locked dependencies, official npm run build, no fork source/node_modules/grammar reuse. Both
upstream source trees remain pristine. All18 fresh indexes for this investigation completed
successfully. JSON result files retain source identities and runtime component hashes.
The extra release-only real-export failure is important: do not claim that all six records
match across all versions. The current-main/em.40 missing-export defect is the remaining case.
Controls
| Case | em.40 / upstream main | Upstream v1.6.2 |
|---|---|---|
| Missing module member, no decoy | Correctly unresolved | Correctly unresolved |
| Same-name unimported class method | Wrong Decoy::missing call | Same wrong call |
| Same-name standalone exported function instead of a method | Correctly unresolved | Correctly unresolved |
| Real exports.missing function beside decoy | Correctly calls utils.js:missing | Wrongly calls decoy |
| Different-name class method | Correctly unresolved | Correctly unresolved |
| Equivalent import * as utils namespace syntax | Correctly unresolved | Correctly unresolved |
No package installation or external dependency is involved in this minimal source. This
distinguishes it from the first verified external-package/dependency-metadata bug.
Source explanation in em.40 / current upstream main
- src/resolution/import-resolver.ts:2194–2207 represents a plain require binding with
isDefault:true and isNamespace:false. - The import resolver can resolve a real exported member; that succeeds in the current-main
real-export control. When the member is absent, resolution continues without a target. - src/resolution/name-matcher.ts:25029–25037 rejects method guessing for imported module
receivers only when isImportedModuleReceiver says it owns the receiver. - isImportedModuleReceiver at name-matcher.ts:25387–25392 accepts namespace bindings or
out-of-repository imports. A default binding from a local relative require is neither. - The code reaches the global method-by-name strategy at name-matcher.ts:25152–25177.
Its single-method path around25253–25280 can choose Decoy::missing without receiver evidence.
This explains the observed class-method-versus-function distinction and the successful ESM
namespace control. It is a fallback after a known module member lookup, not an absent import
mapping or dropped receiver.
A correction must distinguish a known CommonJS module whose member is absent from a genuinely
unknown object receiver. Preserve real exported members and legitimate declared default
class/object/instance APIs. Do not simply turn all CommonJS default bindings into ESM namespaces;
CommonJS can export a class, callable or instance as its stated default.
Related upstream work and novelty limits
- JavaScript: every mod.x() through a CommonJS require links to the module's one anonymous exports.y = function colbymchenry/codegraph#2952 is closed; its original problem was
redirecting arbitrary CommonJS members onto the module's first anonymous exported function. - fix(js,ts): a member of a required or default-imported module is that module's export colbymchenry/codegraph#3162 addressed module export lookup. The fixture
is labelled fixed on f72c9d2. Our current-main real-export control agrees with that fix.
The newly isolated counterexample instead falls through to an unrelated class method when
the module has no matching member. This is a follow-up variant, not a claim that fix(js,ts): a member of a required or default-imported module is that module's export colbymchenry/codegraph#3162 did nothing. - The broader existing issue family may overlap; this investigation does not establish global novelty.
The original decoy lead is in QA report PR27:
https://github.com/inth3shadows/codegraph-qa/pull/27, issue-2952 / decoy, where utils.etag and
utils.missing gained unrelated class-method targets. This reduction isolates the missing-member
part only; factory-produced callable export recovery was not investigated.
Status and reporting plan
Local fork tracking only; no engine edit, fix commit, deployment or official-upstream submission is included. Native mode, published release packages, other languages and real-project prevalence remain unverified.
Official upstream submission is planned for Tuesday, November 10, after final verification and duplicate/related-issue review. This is a remaining method-fallback variant of the broader CommonJS issue family, not a claim of entirely novel behavior.
Activity
inth3shadows commentedon Nov 11, 2026
Filed upstream as colbymchenry#3272 (2026-11-11), re-verified on upstream main f72c9d2. The fix (#28 + #29) stays on the fork for now: it adds
typescriptas a runtime dependency and costs about +38% indexing time on Express.