Skip to content

codegraph exports/dead-role consumer counting ignores type-only (import type) usages #1724

Description

@carlos-alm

Summary

codegraph exports <f> -T --json (and the dead-export classification it feeds) reports zero-consumer / dead-export for interfaces and types that are demonstrably imported and used elsewhere via import type { X } from .... The underlying import graph (codegraph deps) correctly records the cross-file edge — the gap is specifically in how exports/dead-role attributes "consumers."

Repro

$ codegraph exports src/domain/graph/builder/cha.ts -T --json

Shows:

{
  "name": "ChaContext",
  "kind": "interface",
  "line": 18,
  "consumers": [],
  "consumerCount": 0
}

But:

$ codegraph deps src/domain/graph/builder/cha.ts --json

Correctly shows importedBy: [src/domain/graph/builder/stages/build-edges.ts, src/domain/graph/builder/stages/native-orchestrator.ts], and grepping those files confirms import type { ChaContext } from '../cha.js' followed by real usage as a type annotation (e.g. build-edges.ts:47,692,804,1470, native-orchestrator.ts:49,1148,1273).

So the same underlying file-level import edge is correctly tracked by deps, but exports's per-symbol consumer count doesn't credit import type-only usages, producing a false "dead export" for ChaContext.

Same root cause reproduces on other type-only exports, e.g. ExtractParametersOptions.typeMap in src/extractors/helpers.ts.

Impact

Any interface/type that is only ever used as a type annotation (never constructed or called) — a very common and idiomatic TypeScript pattern — is misclassified as dead code by codegraph roles --role dead / flagged with consumerCount: 0 by codegraph exports, even when actively relied upon across the codebase.

Suggested fix

When computing per-symbol consumers for exports, credit import type { X } edges (and other type-position usages: type annotations, generic type arguments, extends/implements clauses) as consumers, not just call/construct edges. The file-level import graph already has this data (as shown by codegraph deps) — the gap is in how the per-symbol consumer list is derived from it.

Workaround used (during /titan-gauntlet audit)

Cross-checked exports-flagged dead types/interfaces against codegraph deps importedBy lists + manual grep for import type before treating anything as a genuine dead-code finding.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions