Repository navigation
Description
Is your feature request related to a problem? Please describe.
The JSON and Neo4j projections disagree about the primary text, and the graph is the one that is wrong. This is the same defect codeanalyzer-java is fixing in codellm-devkit/codeanalyzer-java#254 / #255, and codeanalyzer-typescript in codellm-devkit/codeanalyzer-typescript#201. Filing the python half so all three are tracked; nothing currently covers it here — #203 is the other half of the parity sweep (comments, positions, call-site facts), not this one.
Read from this repo's own checked-in schema.neo4j.json, so these are the emitted facts rather than a reading of the projection code:
:PyModulecarries nosource. Its properties are{id, module_name, file_key, content_hash, file_size, last_modified}. The onlysourceproperty on any node is on the language-neutral:Artifact, so no declaration node's text is reachable on the graph.- No span carries columns or byte offsets. Across the 13 node labels,
start_line/end_lineappear on 6 (PyCallable,PyClass,PyAttribute,PyVariable,PyBodyNode,ConfigKey);start_column,end_column,start_byteandend_byteappear on zero. codeis a derivation of a source the graph does not hold. It rides:PyCallableand:PyClass— two labels — so a class or function resolves to text and nothing narrower does.
The canonical model makes the module's whole-file source the primary text and every narrower node's text a UTF-8 byte slice of it. This repo already implements that meaning on the JSON side (_span_code slicing source.encode("utf-8")[lo:hi]), so the byte offsets exist upstream and are dropped at projection time.
The invariant being restored: the JSON payload and the graph carry the same facts. Re-expressing a JSON field as an edge or a flattened property is fine; silently dropping one is not.
Describe the solution you'd like
-
:PyModulecarriessource, the whole file, matching what the JSON projection carries -
sourceis always present, never absent — an empty file yields"", so a consumer can never confuse "not carried" with "empty" - every spanned node gains
start_column,end_column,start_byte,end_bytebeside its line pair - the four names are exactly those spellings — codeanalyzer-java coins them in fix(neo4j): carry module.source and span byte offsets so the graph can resolve text codeanalyzer-java#255, and a term coined twice is permanently wrong under the cross-language parity clause. Adopt, do not re-coin
- the checked-in
schema.neo4j.jsonis regenerated - a conformance test asserts each module node's
sourceequals the JSON module source per module rather than by presence
Describe alternatives you've considered
Not stated in the original issue.
Additional context
Scope boundary
This analyzer's Neo4j projection only. Does NOT touch the JSON projection, which is already correct. Does NOT drop code, which consumers read even though this change makes it derivable. Does NOT move schema_version, held at 2.0.0 per codellm-devkit/.github#50.
Caveats
- Blocked on fix(neo4j): carry module.source and span byte offsets so the graph can resolve text codeanalyzer-java#255 merging. That PR is open and unreviewed at time of filing, so its spellings are proposed rather than shipped. fix(neo4j): carry body_span, type parameters, leaf spans and call-site facts codeanalyzer-java#258 is stacked on it and modifies the
end_byteline it adds, so the names are load-bearing in java's stack but not final. Shipping before that review settles risks being the second coining. - Node payloads grow by the size of the source tree, partly offset by what
codealready stores redundantly. schema_versiondoes not move, so the addition is presence-detectable only.:PyModulehas no line span at all, so unlike the sibling analyzers this one is adding a span to that node rather than widening one.
Definition of done
- Every module node carries
source, and each one's hash matches thecontent_hashthat same node declares — the check that the value survived serialization rather than merely being present. source.encode("utf-8")[start_byte:end_byte]equals thecodeproperty already on the node, byte-for-byte, for every:PyCallableand:PyClasswhere both are present, with zero mismatches.- At least one fixture module contains a non-ASCII character before a declaration, so a char-offset regression fails that check instead of passing by coincidence.
- Emitted schema matches the checked-in snapshot; existing suite green.
Activity