Repository navigation
feat(semantic-enrichment): real declaration names, per-language calls, structured annotations, notable strings - #258
Merged
Conversation
added 2 commits
November 13, 2026 22:07
Its code (Calls relation, mkCallEdge, TS-only callTypes) already shipped to main, but the change folder was never archived, so the calls-edges requirement never merged into the canonical extraction spec. Archiving now unblocks the extraction-semantic-enrichment change, whose calls-edges delta is written as MODIFIED against this exact requirement.
…, structured annotations, notable strings Fineract qualification showed the tree-sitter path was structurally correct but semantically hollow: methods labeled by their return type instead of their own name, zero calls edges outside TS/JS, annotations flattened into unreadable signature text, and no env/config/topic visibility. - Field-aware AST: TSNodeInfo now carries each child's tree-sitter grammar field name (childByField/childrenByField), surfacing data the binding already provided but Core.hs discarded. - extractIdentifier prefers the grammar's own `name` field (falling back to a C/C++ declarator-field chain, then the old type-list scan with `identifier` preferred over `type_identifier`), fixing both method and field names being mislabeled by their declared type. Removed `type_identifier` from apiSurfaceTypes (AVI-1440: references emit no node). Fixed the per-file collision remap to key on (base id, line) instead of base id alone, so overloaded siblings each keep their own `contains` edge instead of one silently shadowing the other. - `calls` edges now fire for every supported grammar's own call-node vocabulary (java, c-sharp, php, python, ruby, go, rust, c/cpp, ts/js), not just TS/JS call_expression/new_expression. Callee resolution is field-based with a rightmost-name rule for member/qualified calls, resolving to the called method rather than the receiver. Call-target indexing now covers definitions nested in classes/namespaces, not only top-level ones. - Annotations (Java fields, Python/TS decorators) are captured as a structured nodeExtra.annotations array, with a references edge to the resolved imported symbol and a distillation table surfacing route/httpVerb/cron/fixedDelay/topics/configKeys directly on the node. Signatures are now declaration headers (annotations/modifiers/name/ params/return type) instead of full text that swallowed the body. - Notable strings: context-gated env-reads, @value config placeholders, and @KafkaListener/@RabbitListener topics resolve to canonical corpus-global env:/config:/topic: nodes with references edges, working at every granularity including opaque function bodies. See openspec/changes/extraction-semantic-enrichment for the full design, verified grammar matrix, and known limitations (PHP/Rust/C# annotations, cross-file resolution for non-TS-style import syntax, Kotlin grammar vendoring).
added 3 commits
November 13, 2026 22:45
…ask 5.3) Shallow-cloned apache/fineract (7250 .java files) and ran a main-built "before" binary (separate worktree) against this change's "after" binary on the identical checkout, same flags. Confirms every diagnosed gap at real-world scale: 0 -> 20,709 calls edges, 5,803/40,059 (14.5%) -> 0 return-type-mislabeled methods, 2,824 -> 0 type_identifier leak nodes, 0 -> 33,920 nodes with structured annotations (829 httpVerb, 782 route, 37 configKeys, 1 topics), 0 -> 39 canonical Env/Config/Topic nodes. Also found and documented (not fixed, pre-existing, out of scope): an identifier-less Tier-3 node (if_statement etc.) gets baseId = "" with no per-file prefix, so its id after #L-line suffixing can collide across files sharing a line number in the corpus-wide merge; and @scheduled(fixedRate=...) /initialDelay isn't covered by namedArg (only cron/fixedDelay are).
…g) as investigated and blocked Tried vendoring fwcd/tree-sitter-kotlin at tag 0.2.11, the only revision whose grammar.js generates at LANGUAGE_VERSION 11 with this repo's CLI 0.16.9 process. It has a real bug, not a cosmetic one: `return foo(x)` never parses as a call_expression (confirmed via tree-sitter parse: the identical call as a bare statement or a property RHS parses correctly; wrapped in `return` it does not), unchanged across every release from 2021 through at least 2026-04 (grammar.js had zero commits in that whole window). The upstream HEAD fixes it, but requires ABI 14: patching past the one Unicode-regex blocker hits a second, deeper wall (an unresolved grammar conflict the old CLI's resolver can't auto-resolve). The actual root constraint is the Haskell tree-sitter package itself (0.9.0.3, the latest on Hackage) being capped at ABI 11 - the same ceiling vendor/README.md already documents for cpp/nix. Not a Kotlin-specific gap, so not a Kotlin-specific fix; closing it needs a cross-cutting ABI upgrade across every vendored grammar, tracked as a separate, future proposal.
Resolves a textual-only conflict in openspec/specs/extraction/spec.md: both this branch (archiving AVI-1447) and main (merged via #259, archiving AVI-1765) appended independent, non-overlapping requirements at the same insertion point. Kept both — 11 requirements total, openspec validate --all --strict passes for spec/extraction and spec/import-resolution.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Qualification on Apache Fineract (Java, ~4k files) showed the tree-sitter path produces a structurally correct but semantically hollow graph. This closes the four diagnosed gaps.
The gaps (reproduced on a 20-line annotated Java fixture before any fix)
ClientDatawas labeledClientData, not its own name —extractIdentifierpicked the first identifier-class child, and the return-typetype_identifierprecedes the name positionally. Every method sharing a return type collided on one base id, and thecontainsremap (Map.fromList, last-wins) silently dropped containment edges for all but one.callsedges outside TS/JS.callTypesonly matchedcall_expression/new_expression. Java/C#/PHP/Python/Ruby call nodes never matched, and the callee heuristic ("first identifier in the subtree") returned the receiver ofreceiver.method(), not the method.@Path(...),@GET,@Scheduled(...)survived only as the head of a flattened signature that also swallowed the method body — no queryable route, verb, cron, or config key.System.getenv("X")never reached the graph since at Function granularity (the default) method bodies are opaque.A fifth, found during diagnosis: bare
type_identifierreferences leaked as nodes with empty ids.The fix
TSNodeInfonow carries each child's tree-sitter grammar field name — the Hackage binding already populates it,Core.hsjust discarded it.childByField/childrenByFieldlet every other fix below key off grammar semantics instead of positional guessing.extractIdentifierprefers the grammar'snamefield, with a C/C++ declarator-field-chain fallback and a type-list fallback that prefersidentifierovertype_identifier.type_identifierremoved fromapiSurfaceTypes(AVI-1440: references emit no node).(base id, line), not base id alone, so overloaded methods each keep their owncontainsedge.callsedges fire for every supported grammar's own call vocabulary (java, c-sharp, php, python, ruby, go, rust, c/cpp, ts/js). Callee resolution is field-based with a rightmost-name rule, sosvc.save()targetssave, notsvc. Call-target indexing now covers class members, not just top-level definitions.nodeExtra.annotationsarray, plus areferencesedge to the resolved import and a distillation table (route,httpVerb,cron,fixedDelay,topics,configKeys). Signatures are now declaration headers, not full text.@Valueplaceholders, and@KafkaListener/@RabbitListenertopics resolve to canonicalenv:/config:/topic:nodes withreferencesedges, working at every granularity (including the opaque-body default).How it was verified
graphos.cabal) and readingsrc/node-types.jsondirectly — not recollection. Two corrections this surfaced: Ruby's real invocation node ismethod_call(not barecall); PHP'sobject_creation_expressionhas no fields at all.cabal testtargeted runs: 153 examples, 0 failures, 1 pre-existing pending, across every touched describe block.cabal build allis clean across every component..javafiles, shallow clone) — amain-built binary vs this branch's binary, identical flags, identical checkout. Seeopenspec/changes/extraction-semantic-enrichment/FINERACT_VERIFICATION.mdfor the complete table. Headline, at real-world scale:callsedges 0 → 20,709; methods mislabeled with a bare return-type name 5,803/40,059 (14.5%) → 0;type_identifierleak nodes 2,824 → 0; 33,920 nodes gained a structuredannotationsarray (829httpVerb, 782route, 37configKeys, 1topics); 39 canonicalEnv/Config/Topicnodes appeared where there were none.Bugs found and fixed along the way (not in the original plan)
field_declaration.declarator→variable_declarator.name: the field-name fix for methods had the same positional bug for fields (private String tenantHost;labeledString).method/singleton_methodwere missing fromfunctionBoundaryTypes, so every Ruby call's enclosing scope resolved to the module instead of the method.arguments-field value (anargument_listnode), not as a direct child of the call — the first version silently matched zero env-reads until this was corrected.Known limitations (see
openspec/changes/extraction-semantic-enrichmentfor full detail)calls/referencesshare only understands TS/JS/Python's quoted import shape; these five grammars' unquoted form belongs to the separateImports.hspipeline, not wired in here.attribute_item/C#attribute_listare not wired intonodeExtra.annotationsthis pass.node-types.jsonvendored for it) — falls back to positional scan.fwcd/tree-sitter-kotlinrevision that generates at the required ABI (tag0.2.11, 2021) has a real parsing bug (return foo(x)never produces acall_expression), unchanged through every release to date; the upstream fix requires ABI 14, which the Haskelltree-sitterpackage (latest:0.9.0.3) can't load — the same ceiling already documented for cpp/nix. Closing this needs a cross-cutting ABI upgrade across every vendored grammar, not a Kotlin-only fix. Full investigation in tasks.md task 6.1.if_statementetc.) share a bare, kind-agnostic, cross-file-collidable id space; and Fineract's@Scheduled(fixedRate=...)/initialDelayusages aren't covered by thecron/fixedDelay-only distillation.🤖 Generated with Claude Code