You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
canonical-schema.md under-specifies several node fields that every analyzer already produces, so each language is inventing its own names (or silently dropping the data). Discovered doing a field-by-field v1→v2 comparison while migrating codeanalyzer-java to schema v2 (codellm-devkit/codeanalyzer-java#180, epic codellm-devkit/.github#42).
Sibling of #39, which covers the same class of problem for entrypoint/CRUD enrichment. This issue covers the core L1 node vocabulary.
Gap
Keystone today
Evidence from analyzers
Comments / docstrings
no comment vocabulary anywhere
codeanalyzer-python v2 has comments: List[PyComment] on module/class/callable; Java v1 has Comment on nearly every node and its Neo4j schema declares a docstring property on JType/JCallable/JField/…
Local variables
silent
codeanalyzer-python v2 has local_variables: List[PyVariableDeclaration] on callable; Java v1 has Callable.variableDeclarations
Enum constants / type members
silent (type lists only callables{} + fields{})
Java v1 has Type.enumConstants; TS models enums as their own type kind
Call-node richness
call body node is only { callee, arguments }
Java v1's CallSite carries 14 more fields (receiver_expr, receiver_type, argument_types, argument_expr, callee_signature, is_static_call, is_constructor_call, access flags). The Python pilot did not lose these — it emits body call nodes and keeps a parallel rich call_sites[]. So the canonical shape as written forces a data loss that no analyzer actually wants, and the one v2 implementation quietly worked around it
Parameter modifiers
param = { name, type, span, is_variadic? }
Java v1 has ParameterInCallable.modifiers (final)
code_start_line
silent
codeanalyzer-python v2 has code_start_line; Java v1 has codeStartLine
is_implicit
silent
Java v1 has Callable.isImplicit (compiler-generated default constructors)
Record components
silent
Java v1 has Type.recordComponents (with defaultValue, isVarArgs) — arguably language-specific, but worth a ruling (fields? params? own list?)
The pattern is the same one the parity clause exists to prevent: the shared vocabulary is silent, so analyzers coin their own names (or lose the data), and the SDK cannot model the concept once.
Scope boundary
This issue decides and documents the canonical vocabulary in skills/designing-cldk-changes/references/canonical-schema.md (plus consequent notes in neo4j-projection.md / schema-contract.md). It does not implement anything in an analyzer or
SDK — those are follow-ups per repo. Entrypoint/CRUD enrichment is #39, not here.
Goals
Decide canonical comment/docstring representation: a comments[] node list, a single docstring, or "recover from module.source only" — and on which node kinds
Decide whether local variables are a first-class callable field (pilot precedent) or subsumed by L3 body statements
Decide enum constants (and other type members) — new node kind under fields{}, own list, or language-additive
Resolve the call-node richness question: enrich the canonical call node, sanction a parallel call_sites[], or explicitly declare the detail out of scope (and accept that framework/CRUD finders need receiver_type)
Rule on the small ones: parameter.modifiers, code_start_line, is_implicit, record components
For each: state the Neo4j projection consequence, and the migration for analyzers already emitting a divergent shape
Caveats and known risks
Several of these interact with the "absent = no fact" and "no per-callable code" conventions — e.g. dropping comments is defensible becausemodule.source is retained, but then docstrings are not addressable/queryable, which matters for LLM/RAG consumers.
Enriching the call node duplicates data that L4's actual_in/actual_out vertices partly express; the ruling should say which is authoritative.
Whatever is chosen, at least one shipped analyzer migrates: Java v1 and the Python v2 pilot differ here today.
Problem
canonical-schema.mdunder-specifies several node fields that every analyzer already produces, so each language is inventing its own names (or silently dropping the data). Discovered doing a field-by-field v1→v2 comparison while migratingcodeanalyzer-javato schema v2 (codellm-devkit/codeanalyzer-java#180, epic codellm-devkit/.github#42).Sibling of #39, which covers the same class of problem for entrypoint/CRUD enrichment. This issue covers the core L1 node vocabulary.
codeanalyzer-pythonv2 hascomments: List[PyComment]on module/class/callable; Java v1 hasCommenton nearly every node and its Neo4j schema declares adocstringproperty onJType/JCallable/JField/…codeanalyzer-pythonv2 haslocal_variables: List[PyVariableDeclaration]on callable; Java v1 hasCallable.variableDeclarationstypelists onlycallables{}+fields{})Type.enumConstants; TS models enums as their own type kindcallbody node is only{ callee, arguments }CallSitecarries 14 more fields (receiver_expr,receiver_type,argument_types,argument_expr,callee_signature,is_static_call,is_constructor_call, access flags). The Python pilot did not lose these — it emitsbodycall nodes and keeps a parallel richcall_sites[]. So the canonical shape as written forces a data loss that no analyzer actually wants, and the one v2 implementation quietly worked around itparam = { name, type, span, is_variadic? }ParameterInCallable.modifiers(final)code_start_linecodeanalyzer-pythonv2 hascode_start_line; Java v1 hascodeStartLineis_implicitCallable.isImplicit(compiler-generated default constructors)Type.recordComponents(withdefaultValue,isVarArgs) — arguably language-specific, but worth a ruling (fields? params? own list?)The pattern is the same one the parity clause exists to prevent: the shared vocabulary is silent, so analyzers coin their own names (or lose the data), and the SDK cannot model the concept once.
Scope boundary
This issue decides and documents the canonical vocabulary in
skills/designing-cldk-changes/references/canonical-schema.md(plus consequent notes inneo4j-projection.md/schema-contract.md). It does not implement anything in an analyzer orSDK — those are follow-ups per repo. Entrypoint/CRUD enrichment is #39, not here.
Goals
comments[]node list, a singledocstring, or "recover frommodule.sourceonly" — and on which node kindsbodystatementsfields{}, own list, or language-additivecallnode, sanction a parallelcall_sites[], or explicitly declare the detail out of scope (and accept that framework/CRUD finders needreceiver_type)parameter.modifiers,code_start_line,is_implicit, record componentsCaveats and known risks
code" conventions — e.g. dropping comments is defensible becausemodule.sourceis retained, but then docstrings are not addressable/queryable, which matters for LLM/RAG consumers.callnode duplicates data that L4'sactual_in/actual_outvertices partly express; the ruling should say which is authoritative.Definition of done
canonical-schema.mdcovers each row above (field name, shape, placement, Neo4j projection).