A report authored by Claude on missing 1st-party module-level imports in Swift AST parsing:
GraphifyModuleParsingIssue-repro.zip
Swift imports edges are silently dropped from the final graph
Summary
On a clean, from-scratch build, 100% of Swift import statements produce zero imports edges in
graphify-out/graph.json. The AST extractor does emit the edges, but it emits them pointing at a
bare module-name target node (e.g. corekit) that it never creates. build_from_json then drops
every edge whose target isn't a known node — treating first-party module imports as
"external/stdlib" — so all of them vanish.
This contradicts the documented behavior
(https://graphify.net/tree-sitter-ast-extraction.html: "import edges are module-level… extracted
without LLM"). Other Swift relations (calls, implements, inherits, references, method,
contains) extract correctly — the gap is specifically module-level import resolution.
Environment
- graphify 0.8.36 (installed via
uv tool, Python 3.11)
- tree-sitter-swift
- macOS (Darwin 25.3.0)
- Clean, from-scratch AST extraction (no
graphify update, no prior curated state)
Minimal reproduction
A self-contained anonymized sample is attached. It is an Xcode app target plus a local SwiftPM
package with four cross-importing library modules, each exporting a public type named after its
module (mirrors a real monorepo).
GraphifyModuleParsingIssue/ # Xcode app target (imports FeatureAKit, FeatureBKit)
GraphifyModuleParsingIssue/ContentView.swift
GraphifyModuleParsingIssue/GraphifyModuleParsingIssueApp.swift
Packages/
Package.swift # 4 library products/targets
Sources/CoreKit/CoreKit.swift
Sources/NetworkingKit/NetworkingKit.swift # import CoreKit
Sources/FeatureAKit/FeatureAKit.swift # import CoreKit, NetworkingKit
Sources/FeatureBKit/FeatureBKit.swift # import CoreKit, FeatureAKit
There are 7 internal import <Module> statements across these files (plus import SwiftUI and
import PackageDescription). A correct graph should contain ~7 corresponding imports edges.
Steps
- Build the graph from scratch (AST extraction; this is a code-only corpus).
- Run
check.py (included) against graphify-out/graph.json.
Expected
~7 imports edges (e.g. FeatureAKit -> CoreKit, FeatureBKit -> FeatureAKit, etc.).
Actual
type=module nodes: 0 -> []
module dirs on disk: 4 -> ['CoreKit', 'FeatureAKit', 'FeatureBKit', 'NetworkingKit']
imports: 0 | references: 2 | inherits: 1 | implements: 1 | calls: 4
import edges:
Zero imports edges. Zero type=module nodes. Every other relation type extracted fine.
Root cause
The edges are produced by the extractor, then dropped at graph-build time.
1. The Swift import handler emits an edge to a module-name node it never creates.
_import_swift in graphify/extract.py (0.8.36):
def _import_swift(node, source, file_nid, stem, edges, str_path):
for child in node.children:
if child.type == "identifier":
raw = _read_text(child, source)
tgt_nid = _make_id(raw) # e.g. "CoreKit" -> "corekit"
edges.append({
"source": file_nid,
"target": tgt_nid, # <-- no node with this id is ever appended
"relation": "imports",
...
})
break
It appends an imports edge but never appends a corresponding node for the imported module.
(Contrast the JS/TS handlers, which resolve imports to real, existing file-path nodes.) So the
target id (corekit, featureakit, swiftui, …) is always dangling.
2. build_from_json drops every dangling-target edge as "external/stdlib".
graphify/build.py:
if src not in node_set or tgt not in node_set:
continue # skip edges to external/stdlib nodes - expected, not an error
Because every Swift import target is dangling, this prunes 100% of imports edges — including
first-party, in-repo module imports.
Evidence that the edges exist before the drop
Raw extract() output for this corpus contains 10 imports edges (all 7 internal + SwiftUI
×2 + PackageDescription), each with a target id that does not exist as a node:
featureakit_featureakit -> corekit (target exists as node: False)
featureakit_featureakit -> networkingkit (target exists as node: False)
featurebkit_featurebkit -> corekit (target exists as node: False)
featurebkit_featurebkit -> featureakit (target exists as node: False)
networkingkit_networkingkit -> corekit (target exists as node: False)
...
After build_from_json → to_json, the final graph has 0 imports edges.
This is not monorepo-specific
Running extraction on just the Packages/ SwiftPM package (graphify's documented happy path,
no app target) shows the same thing: raw extract() yields 6 imports edges, but after
build_from_json the final graph has 0. The defect applies to any Swift corpus.
Suggested fix
When the Swift import handler emits an imports edge, also emit a node for the imported module
(e.g. type=module, label = module name, id = _make_id(module_name)), the way other languages
anchor imports to a resolvable node. Optionally, parse Package.swift targets/products into
type=module nodes so first-party imports resolve to them while genuinely external modules
(SwiftUI, Foundation) can still be created-or-skipped deliberately rather than dropped by the
generic "dangling edge" pruning in build_from_json.
Attached
- The full sample project (app + SwiftPM package).
check.py — the assertion script that produced the output above.
graphify-out/graph.json and graphify-out/GRAPH_REPORT.md from the from-scratch build.
A report authored by Claude on missing 1st-party module-level imports in Swift AST parsing:
GraphifyModuleParsingIssue-repro.zip
Swift
importsedges are silently dropped from the final graphSummary
On a clean, from-scratch build, 100% of Swift
importstatements produce zeroimportsedges ingraphify-out/graph.json. The AST extractor does emit the edges, but it emits them pointing at abare module-name target node (e.g.
corekit) that it never creates.build_from_jsonthen dropsevery edge whose target isn't a known node — treating first-party module imports as
"external/stdlib" — so all of them vanish.
This contradicts the documented behavior
(https://graphify.net/tree-sitter-ast-extraction.html: "import edges are module-level… extracted
without LLM"). Other Swift relations (
calls,implements,inherits,references,method,contains) extract correctly — the gap is specifically module-level import resolution.Environment
uv tool, Python 3.11)graphify update, no prior curated state)Minimal reproduction
A self-contained anonymized sample is attached. It is an Xcode app target plus a local SwiftPM
package with four cross-importing library modules, each exporting a
publictype named after itsmodule (mirrors a real monorepo).
There are 7 internal
import <Module>statements across these files (plusimport SwiftUIandimport PackageDescription). A correct graph should contain ~7 correspondingimportsedges.Steps
check.py(included) againstgraphify-out/graph.json.Expected
~7
importsedges (e.g.FeatureAKit -> CoreKit,FeatureBKit -> FeatureAKit, etc.).Actual
Zero
importsedges. Zerotype=modulenodes. Every other relation type extracted fine.Root cause
The edges are produced by the extractor, then dropped at graph-build time.
1. The Swift import handler emits an edge to a module-name node it never creates.
_import_swiftingraphify/extract.py(0.8.36):It appends an
importsedge but never appends a corresponding node for the imported module.(Contrast the JS/TS handlers, which resolve imports to real, existing file-path nodes.) So the
target id (
corekit,featureakit,swiftui, …) is always dangling.2.
build_from_jsondrops every dangling-target edge as "external/stdlib".graphify/build.py:Because every Swift import target is dangling, this prunes 100% of
importsedges — includingfirst-party, in-repo module imports.
Evidence that the edges exist before the drop
Raw
extract()output for this corpus contains 10importsedges (all 7 internal +SwiftUI×2 +
PackageDescription), each with a target id that does not exist as a node:After
build_from_json→to_json, the final graph has 0importsedges.This is not monorepo-specific
Running extraction on just the
Packages/SwiftPM package (graphify's documented happy path,no app target) shows the same thing: raw
extract()yields 6importsedges, but afterbuild_from_jsonthe final graph has 0. The defect applies to any Swift corpus.Suggested fix
When the Swift import handler emits an
importsedge, also emit a node for the imported module(e.g.
type=module, label = module name, id =_make_id(module_name)), the way other languagesanchor imports to a resolvable node. Optionally, parse
Package.swifttargets/products intotype=modulenodes so first-party imports resolve to them while genuinely external modules(
SwiftUI,Foundation) can still be created-or-skipped deliberately rather than dropped by thegeneric "dangling edge" pruning in
build_from_json.Attached
check.py— the assertion script that produced the output above.graphify-out/graph.jsonandgraphify-out/GRAPH_REPORT.mdfrom the from-scratch build.