Repository navigation
Description
Problem
The second half of the JSON-vs-graph sweep that produced #255 and #256. Comments are separated out because closing this gap is not a property addition — it needs a decision about what carries a comment on the graph.
analysis.json models a comment as {content, span, is_javadoc} and attaches lists of them to the module, to types, to callables, to fields, to enum constants, to record components, and to individual body nodes. The graph collapses all of that into one docstring string per declaration, built by joining the javadoc entries (V2GraphProjector.docstringOf, line 699). Four distinct facts are lost. Counts from daytrader8 at -a 1:
- File-level comments vanish entirely. 465 comments hang off modules in the fixture;
:JModulehas no comment property at all, so the license header, the file preamble and every free-floating comment between declarations are simply not in the graph. - Non-javadoc comments attached to declarations vanish. 398 comments are attached to types, callables and fields; 328 are javadoc and survive as
docstring; the remaining 70 are dropped becausedocstringOffilters onisJavadoc(). - Every comment's position is lost. The span is discarded when the list joins into a string, so no comment can be located in the file or sliced out of
:JModule.source, which the graph now carries. bodyNode.commentis dropped. 140 of the 4027 body nodes carry one.
The catalog documents the collapse as intentional ("javadoc collapses to a docstring property", V2SchemaCatalog:33), and python-sdk documents living with it: get_all_comments and get_comment_in_file raise rather than answer with a smaller set claiming to be every comment (cldk/analysis/java/neo4j/neo4j_backend.py:92-100). That is an honest workaround for a projection that cannot answer the question, not evidence the projection is right.
The invariant being restored: the JSON payload and the graph carry the same facts. docstring is a legitimate convenience derivation; it is not a substitute for the comments it was derived from.
Scope boundary
The comment model on the Neo4j projection. docstring stays, whatever shape this takes — consumers use it and it costs nothing to keep as a derived property.
Does NOT touch the JSON projection, which is already correct. Does NOT move schema_version, held at 2.0.0 per codellm-devkit/.github#50.
Goals
- decide the shape: a
:JCommentnode family reached by a containment edge from whatever it is attached to, versus a serialized-JSON property per declaration (theparameters_jsonprecedent), versus parallelcomments/comment_spanslist properties - file-level comments are reachable from
:JModule - non-javadoc comments are carried, distinguishable from javadoc ones
- each comment's span is carried, so it can be located in the file and sliced out of
:JModule.source -
bodyNode.commentis carried -
docstringis retained on every declaration that has it today -
V2SchemaCatalogdeclares the shape,schema.neo4j.jsonis regenerated, and the conformance test asserts comment counts per node against the JSON rather than by presence
Caveats
- This is a contract-shape decision, not a property addition. If the answer is a new node family, it adds a label and an edge type to a shared graph contract, which is schema v2 evolution and belongs in design mode rather than in-place work. Triage it before implementing.
- Whatever shape is chosen, codeanalyzer-python and codeanalyzer-typescript inherit it. Both are worse off than this analyzer today: python projects a
docstringper class/callable/attribute and drops module comments (codeanalyzer/neo4j/project.py:783,806,825,854), and codeanalyzer-typescript projects no comment or docstring property anywhere at all. A shape decided here is the shape all three must spell the same way. schema_versiondoes not move, so the addition is not detectable from the payload. Detection is by presence.- The cheap-looking option is the lossy one. Joining comments into a string is exactly what produced this defect; a shape that keeps content and span together is the point.
Definition of done
- Every comment in the fixture's
analysis.jsonis reachable on the graph, counted per module and per declaration, with zero shortfall. - Each comment's carried span slices
:JModule.sourceto that comment'scontent, byte-for-byte — the check that proves the position survived rather than merely being declared. docstringvalues are unchanged from before the change on every node that had one.python-sdk's reason for raising inget_all_commentsno longer holds, stated explicitly in the closing note so the SDK-side uptake can be filed against it.- Emitted schema matches the checked-in snapshot; existing suite green.
Activity
rahlk commentedon Sep 10, 2026
Closing as
wontfix. Comments are globally ignored: the ceiling is accepted across the analyzers, not just deferred here, sodocstringon the graph is where the comment model stops.What that means for the surrounding work, so it is not rediscovered:
JSON == Graphinvariant holds for everything else. Comments are the one carved-out exception, made explicitly rather than by omission.python-sdk'sget_all_comments/get_comment_in_fileraising on the Neo4j backend is now the permanent answer for that backend, not a workaround waiting on this issue. The in-memory backend remains the way to reach comments.V2SchemaCatalog's note that javadoc collapses todocstringis therefore accurate as written, not a TODO.Reopen if a consumer turns up that needs comment text or positions out of the graph.