Symbol identity
Every symbol is named by a SCIP-style structured descriptor. Identity is one human-readable string, and resolution is a string match rather than an opaque id lookup.
A descriptor is five parts —scheme manager package version descriptor-path — of which the first four are the package coordinate and the last is the structural path to the symbol inside it.
The suffix grammar is SCIP's: # closes a type,() a method or function, . a term such as a field or a variable. So internal/graph/Store# is the type,internal/graph/Store#db. its field, andinternal/graph/Store#Put(). its method — three descriptors that sort next to each other and read without a lookup table.
Why the split matters
The whole point of the split is to keep extraction file-local. A stanza runs against one CST and knows nothing about the project it is in, so it can only honestly produce the structural suffix. The package coordinate is a property of the project, so a per-ecosystem manifest resolver readsgo.mod (then package.json, and so on) once per batch and maps each file path to its coordinate. The extractor stays dumb; the descriptor still comes out complete.
That coordinate is exactly what the four pkg_ columns onfile hold — see the core model.
Same-file versus cross-file
A reference whose target definition is in the same CST is resolved at extraction time and gets a references_local edge on the spot. A reference whose target is in another file cannot be resolved by a file-local extractor, so the row is written with the target's descriptorunresolved — the descriptor column is meaningful on both roles — and the link pass matches it later:
The "symbol index" that makes this fast is not a separate structure. It is the btree on occurrence.descriptor that the SDL declares, and nothing else.
Worked example, from the seed
cmd/codiq/main.go writes graph.Store{…} and then calls store.Put(…). Both are cross-file, so extraction emits two reference occurrences carrying the descriptors of definitions it has never seen:
The link pass joins those pairs and writes two resolves_toedges. Nothing about main.go's rows had to know thatstore.go exists, which is precisely what makes re-indexing one file independent of the other.