Skip to content

feat(neo4j): derive code onto declarations, matching python and java - #136

Closed
rahlk wants to merge 1 commit into
fix/issue-118-content-hashfrom
feat/graph-code-projection
Closed

feat(neo4j): derive code onto declarations, matching python and java#136
rahlk wants to merge 1 commit into
fix/issue-118-content-hashfrom
feat/graph-code-projection

Conversation

@rahlk

@rahlk rahlk commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #119 (which is stacked on #117) — review those first; this PR's diff is the projection change on top.

The gap

TypeScript source text was unreachable from the graph:

:TSCallable no code property
:TSModule no source property to slice from
nodes start_line/end_line only — not the span.bytes the tree uses

Neither route worked. python and java hand a consumer code directly; TypeScript gave it nothing.

The JSON model was never the disagreement

This is the part worth being precise about, because it changes what the fix is:

JSON Neo4j code
python module source once + span.bytes; no per-callable code derived at projection
typescript module source once + span.bytes; no per-callable code was missing
java code stored per callable carried through

python and TypeScript already agree on the JSON model. python then completes the contract on the
graph side (neo4j/project.py::_span_code):

"A declaration's text: the owning module's source sliced by the node's utf-8 byte span. Schema
v2 stores source once per module, so the graph's code property … is derived here at projection
time (#104)."

TypeScript stopped short of that step. This adds it — no JSON change, no text stored twice.

Details matched deliberately

  • spanCode slices a Buffer, not the string. span.bytes are UTF-8 byte offsets and JS
    strings are UTF-16, so source.slice() would cut multi-byte characters. The same trap had to be
    fixed in artifact text capture earlier.
  • ts_code_fts, a fulltext index over TSCallable.code, mirroring python's py_code_fts. Text
    on a node that cannot be searched is half a feature.
  • code is declared on every label that receives itTSCallable plus the five type labels,
    since typeProps is shared. python declares it only on PyClass/PyCallable; declaring it where
    it is actually emitted is what keeps the conformance gate meaningful.

Verification

Live graph, sample-app:

callables, with_code
42, 41

The one without is a bodiless declaration (nothing to slice). Sample value:

"Get" → "function Get(path: string): MethodDecorator {\n  return () =>…"

WHERE c.code CONTAINS "..." now works. Full suite with RUN_CONTAINER_TESTS=1: 237 pass, 0 fail, 0 skip.

Related

TypeScript source text was unreachable from the graph. :TSCallable carried no
`code`, and :TSModule carried no `source` to slice from -- nodes had only
start_line/end_line, not the `span.bytes` the tree uses. So neither route
worked, while python and java hand a consumer `code` directly.

The JSON model was never the disagreement: python and typescript both store a
module's text ONCE and address nodes into it by UTF-8 byte span, with no
per-callable `code` field. Python then derives `code` at projection time
(neo4j/project.py::_span_code, "schema v2 stores source once per module, so the
graph's `code` property ... is derived here"). TypeScript simply stopped short
of that step. This adds it.

`spanCode` slices a Buffer, not the string: `span.bytes` are UTF-8 byte offsets
and JS strings are UTF-16, so string slicing would cut multi-byte characters --
the same trap that had to be fixed in artifact text capture.

Also adds `ts_code_fts`, a fulltext index over TSCallable.code, mirroring
python's `py_code_fts`. Text on a node that cannot be searched is only half the
feature.

`code` is declared on every label that receives it -- TSCallable plus the five
type labels, since typeProps is shared -- rather than only the two python
declares, so the conformance gate stays honest.

Verified on a live graph: 41 of 42 callables carry sliced source (the one
without is a bodiless declaration), e.g. Get -> "function Get(path: string):
MethodDecorator {\n  return () =>...". Full suite with containers: 237 pass.

Java's JSON stores `code` per callable instead of slicing; filed as
codellm-devkit/codeanalyzer-java#215.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant