* feat(route): resolve vendor-derived Spring mapping annotations by suffix
Frameworks commonly wrap Spring's built-in annotations with company-specific
variants (e.g. Winning Health's @WinPostMapping wraps @PostMapping). The
annotation definition lives in a binary JAR — not in source — so the
meta-annotation cannot be read statically.
Add resolveSpringAnnotationAlias(): resolves custom annotations by naming
suffix (WinPostMapping → PostMapping → POST). This matches the universal
Java convention of naming derived annotations with the base name as a suffix.
Works for any vendor prefix, not just one company.
The fix is in springAnnotationHttpMethods() (spring-shared.ts), which both
the ingestion extractor (spring.ts) and the group extractor (java.ts) call.
A single-function change propagates to both layers automatically.
Zero configuration: no .gitnexusrc, no annotation allowlist. If an annotation
name ends with a known Spring mapping suffix, it inherits that annotation's
HTTP semantics. False-positive risk is negligible.
Tests: 16 new unit tests covering resolveSpringAnnotationAlias directly,
springAnnotationHttpMethods with aliased annotations, end-to-end
extractSpringRoutes with vendor annotations, and ingestion/group parity.
Existing route tests (260) continue to pass.
* fix(route): address review findings — class-level aliases, registered prefixes
P1: class-level @WinRequestMapping now gets the same prefix/constraint
semantics as @RequestMapping — all five class-level exact-match sites
(spring.ts phase-1 collect, typeRequestMethods, typeClassPrefixes; group
http-patterns java.ts typeRequestMethods + type-level branch) route
through the new shared isClassLevelMappingAnnotation predicate, and the
hard-coded 'RequestMapping' argument in springAnnotationHttpMethods calls
is replaced with the actual annotation name so alias resolution applies.
P2: suffix-only alias matching accepted unrelated annotations
(@AuditPostMapping emitted a phantom POST /audit). Alias resolution now
requires a REGISTERED vendor prefix — 'Win' by default, extendable via
GITNEXUS_SPRING_VENDOR_PREFIXES=Win,Acme without a rebuild.
Tests: negative e2e for the unregistered-suffix phantom route, vendor
class-prefix parity e2e, predicate unit matrix, env-registration test.
* style: prettier
* chore: drop accidental gitnexus-shared/dist worktree symlink from prettier commit
Co-authored-by: Cursor <cursoragent@cursor.com>
* Address PR review feedback (#2883)
Wire vendor Spring mapping aliases into Kotlin ingestion and group extraction, restore GITNEXUS_SPRING_VENDOR_PREFIXES after the env test, and stamp spring.route-bindings so existing indexes rebuild.
Co-authored-by: Cursor <cursoragent@cursor.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
* fix(route): honor Kotlin vendor aliases and prefix freshness
Parse Kotlin RequestMapping method arrays in the shared Spring helper, bump spring.route-bindings, and rebuild when registered vendor prefixes change.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: ChunxueLi <mecoloud@users.noreply.gitee.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
* feat(kotlin): bind Spring @Value and ConfigurationProperties consumers
Kotlin sources were skipping the Java-only config-binding attach path, so mixed JVM apps under-reported blast radius for Kotlin placeholders. Capture from the live AST, serialize on the existing side channel, and reuse the shared binder. (U1-U4)
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(review): require exact Spring annotation FQNs and skip raw-string escapes
Reject similarly named third-party imports and leave Kotlin triple-quoted bodies undecoded so capture stays fail-closed.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(kotlin): use only grammar-valid Kotlin string and class-name nodes
The coverage shard failed the #1920 literal gate on invented string node types and a Java-style name field.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(kotlin): fail closed on non-literal prefixes and persist unresolved config markers
Reject constant and boolean @ConfigurationProperties arguments, scope nested Value shadows to their owner, and rewrite drifted consumer files when a config key is deleted.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(kotlin): add config-consumer capture benchmark and fix JVM feature stamps
The Kotlin capture path had no performance or behavior guard: a file-wide
lexical shadow regression silently dropped two of every three facts. The new
bench arm fingerprints @Value / @ConfigurationProperties facts from an
explicit-import control against a wildcard-import corpus whose files each
declare a sibling nested `Value` type, so parity between the arms is the
regression gate, and CI runs it with scaling + widening budgets.
Broadening spring.config-bindings to .kt also means Kotlin-only JVM repos now
stamp the feature, which the two exact-map orchestration expectations still
denied.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(kotlin): let an explicit import shadow the Spring wildcard import
importedAs accepted a star import of the Spring package even when the same
simple name was explicitly bound to another type, so a file importing
com.example.Value alongside the Spring annotation package emitted a false
@Value consumer fact. Kotlin resolves the explicit import first, so the
wildcard branch now only applies when the name is otherwise unbound.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(java): materialize record component accessors
* fix(java): ignore receiver params in record accessor arity
* fix(java): address record-accessor review findings (#2917)
Five findings from the tri-review of #2936.
P1 — a synthesized callable evicted a source-written one from the method map.
`getMethodInfo` keyed its per-class map by `name:line`, but a callable that is
SYNTHESIZED at a position that is not its own declaration shares its owner's
line: a record's implicit accessor is minted at the component, and a C# 12
primary constructor at the owner's `parameter_list`. Both are appended last by
their extractor, so on a single line the synthesized entry overwrote the
explicit method's MethodInfo and both definitions collapsed onto one id —
`record P(int x, int y) { int x(int s) {...} }` lost `P.x#1` and rebound the
arity-1 call to the zero-argument accessor. Adds a required `MethodInfo.column`
and keys the map by `name:line:column` through a single `methodInfoKey` helper.
Required, not optional: an absent column would key an entry no lookup could
reach — a silent, whole-language loss of enrichment instead of a compile error.
All three lookup sites move together; the file's own lockstep docblock warns
that a half-applied change loses caller edges silently rather than dangling.
This also fixes the same collision in C#, which never touched record code.
Degenerate component names no longer mint a node. tree-sitter's zero-width
MISSING recovery token satisfies `name: (identifier)`, so `record M(int x, y) {}`
minted an empty-named Method whose returnType was the neighbouring `y`; and the
grammar admits `underscore_pattern` in the same field, which the query rejected
but the scope path accepted, so `record R(int _) {}` left a scope declaration
with no node behind it. One `isRecordComponentName` predicate now gates all
three emitters — query suppression, scope synthesis, and the method extractor —
so they cannot drift apart again.
Component annotations reach the implicit accessor (JLS 8.10.3 / 9.7.4) by
reusing the shared `extractAnnotations` helper. Deliberately over-approximate
and commented as such: `@Target` lives in another file and parsing is per-file.
`explicitZeroArgAccessorNames` is memoised per record node. It was rebuilt on
every component capture — O(components x body members) for one record, measured
at ~4x per 2x input — while the scope path already hoisted the identical call.
Docs: the `java-local-types` baseline now stores the `capture_groups_fp` its own
note cites, the SCHEMA_BUMP ledger no longer claims a v65 that nothing holds,
and `shouldSkipDefinitionCapture` documents that `defaultLabel` may be ignored.
Scope-capture fingerprints are unchanged (`measure.mjs --check` PASS, 15
languages): the bench corpus contains no degenerate components, so the new
predicate is inert on it. SCHEMA_BUMP stays 67 — this branch's existing claim
already covers the changed worker output; re-check it against origin/main before
merging.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Px638Zyqa9CJMUU7DsJoB
* docs(ingestion): reunite the overload-suffix JSDoc with typeTagForId
The block describing the `~type1,type2` same-arity discriminator was stranded
above `buildCollisionGroups` when that function was inserted between it and the
`typeTagForId` it documents (#658). Adding `methodInfoKey` in this branch parked
it directly above yet another unrelated function, which gitnexus-check flagged.
Moves the comment down to the function it describes. No behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Px638Zyqa9CJMUU7DsJoB
---------
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>