Skip to content

canonical-schema: define out-of-project call-target vocabulary — external_symbols + @external ids (L2) #41

Description

@sinha108

Problem

canonical-schema.md (the keystone) mandates "No dangling endpoints — every src/dst references node ids" in the Edges section, but gives no vocabulary for a call whose target is outside the project (JDK / library / builtin). Every analyzer has to resolve this to satisfy the invariant, and the keystone's own worked example quietly violates it:

Analyzer / keystone out-of-project call target today
Keystone silent — no external_symbols, no @external id. The worked call_graph example points a Go call at dst: "can://go/myapp/src/fnv.go/New64()" — a stdlib target dressed as a plain source-file id, which dangles under the keystone's own no-dangling rule (there is no fnv.go node in the tree).
codeanalyzer-java (v2, L2) coined an external_symbols{} map at application scope, keyed by an @external can-id (can://java/<app>/@external/<binary-type>/<sig>, valued {kind, signature, declaring_type}); a call_graph dst may be such a key, which is how no-dangling is met for library targets. Ledger D19; homing is opt-in behind --external-calls (default off = v1 parity), D24.
codeanalyzer-typescript / -python no L2 external representation yet — both will need one, and will coin it incompatibly unless the keystone rules.

This is the same parity-clause failure #39 (entrypoint/CRUD) and #40 (L1 node fields) exist to prevent — a shared concept the keystone is silent on, so one analyzer coins it and the others (and the SDK) cannot model it once. It surfaced at L2 (the edge overlay) rather than L1.

Discovered while implementing Java's L2 v2 call graph (codellm-devkit/codeanalyzer-java#181, epic codellm-devkit/.github#42). Java is shipping the external_symbols / @external shape now and will align once this is decided.

Scope boundary

Decide and document the canonical vocabulary for out-of-project call targets in skills/designing-cldk-changes/references/canonical-schema.md (and any consequent notes in neo4j-projection.md / schema-contract.md). This does not implement it in any analyzer or SDK — those are follow-ups per repo — and it adds no detection capability.

Goals

  • Decide the canonical representation of an out-of-project call target: the Java precedent (an external_symbols{} map at application scope, keyed by an @external id, valued {kind, signature, declaring_type}) vs. any alternative — and state explicitly that a call_graph dst may be an external_symbols key, which is how the no-dangling invariant is satisfied for such targets.
  • Fix the worked call_graph example (the Go fnv.New64 target) to use the chosen representation, so the keystone stops contradicting its own no-dangling rule.
  • Specify the @external id grammar in the id section — can://<lang>/<app>/@external/<binary-type-name>/<erased-sig> — and document @ as the reserved marker for non-file id slots (already used for synthetic body-node tags). Rule on binary vs source spelling: Java puts the binary name (java.util.Map$Entry) in the id for exactness/joinability and the dotted form in declaring_type, so consumers never parse the id.
  • Decide whether external homing is mandatory or optional in the canonical output. Java made it opt-in (--external-calls, default off, for v1 parity); with it off, external targets are dropped and no-dangling holds trivially. Rule on whether the default max_level-2 payload should carry externals.
  • Note that prov is per-language open vocabulary (the keystone already says so): Java's L2 values are ["declared", "rta"]; record that each analyzer documents its own prov set.
  • (Neo4j) decide whether an external symbol gets its own node family/label in the projection, or is folded into the existing callable node family.

Definition of done

  • canonical-schema.md defines the out-of-project call-target vocabulary, the @external id grammar, and the dst-may-be-external rule; the worked example no longer dangles.
  • The decision is reflected (or a follow-up filed) for each analyzer and the SDK, and for the Neo4j projection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions