Skip to content

feat(group): add Kotlin Spring HTTP route extraction (named + positional)#1849

Merged
magyargergo merged 2 commits into
abhigyanpatwari:mainfrom
henry201605:feat/kotlin-spring-http-routes
May 27, 2026
Merged

feat(group): add Kotlin Spring HTTP route extraction (named + positional)#1849
magyargergo merged 2 commits into
abhigyanpatwari:mainfrom
henry201605:feat/kotlin-spring-http-routes

Conversation

@henry201605

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #1834 (now merged) — extends the Spring named-argument HTTP route extraction to Kotlin Spring Boot controllers, as suggested by @magyargergo in #1834 (comment).

A new http-patterns/kotlin.ts plugin sits behind the optional tree-sitter-kotlin grammar, registered for .kt / .kts. Both annotation forms produce providers:

@RequestMapping("/api")          @GetMapping("/users")             // positional
@RequestMapping(path = "/api")   @GetMapping(path = "/users")      // named
@RequestMapping(value = "/api")  @GetMapping(value = "/users")     // named

Why two queries

The Kotlin AST (fwcd/tree-sitter-kotlin) shares one node type — value_argument — for both positional and named forms, with an optional leading simple_identifier "=" distinguishing them. So the queries split:

Without the key: constraint, the named query would also capture non-route attributes like produces, consumes, headers, name, params — emitting bogus contracts identical to the Finding A failure mode flagged on #1834.

Optional dependency

tree-sitter-kotlin is an optionalDependency. When the native binding is unavailable (e.g. CI environments where the build fails), the plugin exports null and index.ts skips registering .kt / .kts, so the orchestrator stays healthy.

This mirrors the existing pattern used by parse-worker.ts and parser-loader.ts.

Out of scope

Kotlin consumer detection (RestTemplate / WebClient / OkHttp) is intentionally not included here. The Kotlin call-site ASTs differ enough from Java's method_invocation shape that I felt a focused follow-up PR would be easier to review than bundling it. Happy to open that next.

Tests

11 new cases under provider extraction — source-scan fallback (Strategy B), gated by the kotlin grammar availability:

Positive (8)

  • class @RequestMapping("/api/v1") (positional)
  • class @RequestMapping(path = "/api/v2")
  • class @RequestMapping(value = "/orders")
  • method @GetMapping(value = "/users")
  • method @GetMapping(path = "/users")
  • method @PostMapping(path = "/users")
  • mixed: class named-arg + method positional
  • mixed: class positional + method named-arg

Anti-regression (3)

  • @GetMapping(produces = "application/json") emits no provider
  • @GetMapping(name = "x", value = "/users") emits exactly one provider
  • @RequestMapping(path = "/api", name = "myApi") prefix stays /api, never myApi

I sanity-checked the constraint the other way too: removing (#match? @key "^(path|value)$") makes precisely the 3 anti-regression tests fail — same shape as the Java fix.

Local validation

  • http-route-extractor.test.ts — 54 / 54 ✅
  • test/unit/group — 534 / 534 ✅
  • npx tsc --noEmit — clean ✅

…nal)

Mirror the Java Spring named-argument fix for Kotlin Spring Boot
controllers. Adds a new `http-patterns/kotlin.ts` plugin behind the
optional `tree-sitter-kotlin` grammar, registered for `.kt`/`.kts`.

Both annotation forms produce providers:
  @RequestMapping("/api")          / @GetMapping("/users")
  @RequestMapping(path = "/api")   / @GetMapping(value = "/users")
  @RequestMapping(value = "/api")  / @GetMapping(path = "/users")

The Kotlin AST (fwcd/tree-sitter-kotlin) shares one node type
(`value_argument`) for positional and named forms, so the queries
are split:
  - positional: anchors `string_literal` as the first named child
    of `value_argument` via the immediate-child anchor `.`
  - named: explicitly captures `simple_identifier` and constrains
    it to `^(path|value)$` via `#match?`, mirroring the same
    safety bar enforced by `http-patterns/java.ts` and
    `topic-patterns/java.ts`. Without this constraint the query
    would also capture non-route attributes like `produces`,
    `consumes`, `headers`, `name`, `params`.

`tree-sitter-kotlin` is an optionalDependency (parser-loader.ts,
parse-worker.ts pattern). When the native binding is unavailable
the plugin exports `null` and `index.ts` skips registering
`.kt`/`.kts` so the orchestrator stays healthy.

Scope: providers only. Consumer detection (RestTemplate, WebClient,
OkHttp) on Kotlin call-site ASTs differs enough from Java's
`method_invocation` shape to warrant a separate, focused PR.

Tests: 11 new cases under `provider extraction — source-scan
fallback (Strategy B)`, gated by the kotlin grammar availability.

  positive (8)
   - class @RequestMapping("/api/v1") (positional)
   - class @RequestMapping(path = "/api/v2")
   - class @RequestMapping(value = "/orders")
   - method @GetMapping(value = "/users")
   - method @GetMapping(path = "/users")
   - method @PostMapping(path = "/users")
   - mixed: class named-arg + method positional
   - mixed: class positional + method named-arg
  anti-regression (3)
   - @GetMapping(produces = "application/json") emits no provider
   - @GetMapping(name = "x", value = "/users") emits exactly one provider
   - @RequestMapping(path = "/api", name = "myApi") prefix stays /api

Reverse-validated: removing the `(#match? @key "^(path|value)$")`
constraint causes precisely the 3 anti-regression tests to fail.

Local validation:
  - test/unit/group/http-route-extractor.test.ts: 54/54
  - test/unit/group: 534/534
  - npx tsc --noEmit: clean (modulo the pre-existing TS2339 in
    user-defined-conversions.ts merged from main, unrelated)
@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

✨ PR Autofix

Found fixable formatting / unused-import issues across 94 changed lines. Comment /autofix on this PR to apply them, or run npm run lint:fix && npm run format locally.

{"schema":"gitnexus.pr-autofix/v2","state":"fixes-available","pr_number":1849,"changed_lines":94,"head_sha":"ec5058136288b9aa58b072b5cbc8ea651ff37d03","run_id":"26495803043","apply_command":"/autofix"}

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 5m 30s —— View job


Production-Readiness Review: PR #1849 — Kotlin Spring HTTP Route Extraction

Review complete.


Review bar for this PR

Three lenses: (1) GitNexus core maintainer — source-scan/group contract extraction, optional parser loading, test/CI wiring, DoD enforcement; (2) Kotlin expert — tree-sitter-kotlin AST behavior, annotation argument forms, optional native grammar; (3) Spring Boot expert — route annotation semantics, false-positive resistance, path joining.


Problem being solved

GitNexus Strategy B (source-scan) had no Kotlin Spring Boot support. PR #1834 (merged) fixed Java named-arg Spring route extraction (@RequestMapping(path = "/api")). This PR extends the same fix to Kotlin controllers, where the Kotlin grammar (fwcd/tree-sitter-kotlin) uses value_argument for both positional and named annotation args — requiring a split-query approach analogous to what Java now uses for element_value_pair.


Current PR state

  • Branch: henry201605:feat/kotlin-spring-http-routesabhigyanpatwari:main
  • Commits: 1 clean feature commit (ec50581)
  • Changed files: 3 (all domain-consistent: http-patterns/kotlin.ts new, http-patterns/index.ts +17, test file +313)
  • CI overall: FAILED — 1 job failing, 8 passing

Merge status and mergeability

❌ Not mergeablequality / format (job 78023475347) fails with:

[warn] gitnexus/test/unit/group/http-route-extractor.test.ts
[warn] Code style issues found in the above file. Run Prettier with --write to fix.
Process completed with exit code 1.

All other jobs pass: quality / lint ✅, quality / typecheck ✅, tests / ubuntu / coverage ✅, tests / windows-latest ✅, tests / macos-latest ✅, tests / packaged install smoke (ubuntu + windows) ✅, CodeQL ✅, Dependency Review ✅, Docker Build & Push ✅.


Repository history considered


Branch hygiene assessment

Clean feature PR. One commit, three domain-consistent files. No unrelated formatting churn in unchanged sections. No hidden merge-from-main commits. No package.json/lockfile drift needed (dependency was already declared).

The Prettier failure is in new code only (http-route-extractor.test.ts lines added by this PR).


Understanding of the change

kotlin.ts is a new HttpLanguagePlugin that:

  1. Loads tree-sitter-kotlin via createRequire inside a try/catch; exports null on failure
  2. Inside buildKotlinPlugin() — called only when grammar is non-null — compiles two pattern bundles: class-prefix patterns (2 compiled queries) and method-route patterns (2 compiled queries)
  3. The scan() method builds a prefixByClassId map from class matches, then for each method match walks findEnclosingClass, looks up the prefix, calls joinPath, and pushes an HttpDetection with role: 'provider', framework: 'spring', confidence: 0.8

index.ts conditionally registers .kt/.kts in the REGISTRY only when KOTLIN_HTTP_PLUGIN !== null, and adds kt,kts to HTTP_SCAN_GLOB unconditionally (with a comment explaining that getPluginForFile gracefully returns undefined when grammar is absent).


Findings


Finding 1: quality / format CI failure — test file fails Prettier check

Risk: MERGE BLOCKER

Evidence found: CI job 78023475347 (quality / format) fails at step "Run npx prettier --check .":

[warn] gitnexus/test/unit/group/http-route-extractor.test.ts
[warn] Code style issues found in the above file. Run Prettier with --write to fix.

The PR Autofix bot (run 26495758078) already identified 94 changed lines with fixable issues. DoD.md §6 and §4.2 are explicit: "prettier was bypassed without explicit justification" is a not-done signal and npx prettier --check . is part of the validation baseline.

Recommended fix: Run /autofix on the PR, or locally cd gitnexus && npx prettier --write test/unit/group/http-route-extractor.test.ts and push. This is a one-command fix.

Blocks merge: yes


PR-specific assessment sections

A. Runtime registration and optional grammar safety

✅ Confirmed safe. kotlin.ts exports KOTLIN_HTTP_PLUGIN: HttpLanguagePlugin | null. index.ts only registers .kt/.kts in REGISTRY when KOTLIN_HTTP_PLUGIN is truthy (index.ts:39-42). http-route-extractor.ts:163-169 calls getPluginForFile(rel) and returns [] immediately when !plugin — no crash path. HTTP_SCAN_GLOB includes kt,kts unconditionally, but this only adds files to the glob result; each file's plugin lookup is still guarded. Performance impact of scanning .kt files when grammar is absent is bounded by the getPluginForFileundefined early-return; no parsing occurs.

compilePatterns is called inside buildKotlinPlugin() (not at module-load-time like Java). This is the correct design: calling new Parser.Query(null, ...) would throw. Java can compile at module load because tree-sitter-java is a hard dependency; Kotlin cannot because the binding is optional.

B. Kotlin AST/query correctness

✅ Confirmed correct for the claimed scope.

Positional anchor ((value_argument . (string_literal) @prefix)): The . anchor in tree-sitter query syntax means the following node must be the first named child of its parent. For a positional arg @GetMapping("/users"), value_argument has one named child: string_literal → first named child → matches. For a named arg @GetMapping(path = "/users"), value_argument has simple_identifier as its first named child, followed by string_literalstring_literal is NOT first → does not match. The anchor correctly excludes named forms from the positional query.

Named query key constraint (#match? @key "^(path|value)$"): Directly mirrors the Java plugin's #match? @key "^(path|value)$" on element_value_pair. Confirmed at kotlin.ts:113 and kotlin.ts:152.

No double-match: For any given @RequestMapping(path = "/api"), only the named pattern fires (positional anchor fails because simple_identifier precedes string_literal). For @RequestMapping("/api"), only the positional pattern fires (no simple_identifier in value_argument). One match per annotation argument. prefixByClassId.set is called once per class. No overwrite risk.

findEnclosingClass: Identical semantics to java.ts (kotlin.ts:164-171 vs java.ts:196-203). Uses 'class_declaration' type — correct for Kotlin. Note: Kotlin object declarations use object_declaration in the grammar, so singleton Spring controllers would not have their class-level prefix resolved. This is an out-of-scope edge case for typical Spring Boot usage and is acceptable as a known limitation.

joinPath: Line-for-line identical to java.ts:211-216. Verified at kotlin.ts:179-184.

C. Spring Boot route semantics

✅ Scope is correctly limited and does not overclaim. The PR description explicitly lists what is supported (class positional + named prefix, method positional + named for Get/Post/Put/Delete/PatchMapping). Intentionally excluded: array-value paths (value = ["/a", "/b"]), @RequestMapping on methods (only class-level prefix is extracted), fully-qualified annotations (org.springframework.web.bind.annotation.GetMapping), alias imports, composed annotations, consumer patterns. The PR description is accurate and honest about scope.

D. False-positive/false-negative behavior

✅ Confirmed correct via direct query analysis and confirmed by anti-regression tests.

For @GetMapping(produces = "application/json"):

  • Positional query: produces = ...simple_identifier is first named child of value_argument → positional query does NOT match ✓
  • Named query: key is "produces"#match? @key "^(path|value)$" fails → does NOT match ✓
  • Result: zero routes emitted ✓ (test at test.ts:729-762)

For @GetMapping(name = "listUsers", value = "/users"):

  • value_argument(name = "listUsers"): key is "name" → fails key constraint ✓
  • value_argument(value = "/users"): key is "value" → passes key constraint → one route at /users
  • Result: exactly one route http::GET::/users (test at test.ts:764-799)

For @RequestMapping(path = "/api", name = "myApi"):

  • Named query: path argument matches, prefixByClassId gets /api. name argument fails key constraint.
  • Result: prefix is /api, not myApi (test at test.ts:801-827)

E. Test coverage and CI reality

✅ Tests actually ran in CI — not silently skipped.

The ubuntu/coverage log confirms: test/unit/group/http-route-extractor.test.ts ran 54 tests, 0 skipped in 300ms. Since the pre-PR count was 43 tests (54 − 11 new Kotlin tests), all 11 Kotlin cases executed. tree-sitter-kotlin: ^0.3.8 in optionalDependencies built successfully on ubuntu-latest.

Coverage for http-patterns/kotlin.ts:

kotlin.ts | 87.75 | 70.37 | 100 | 95.23 | 49,170

Uncovered lines: 49 (the Kotlin = null catch branch — expected when grammar is available) and 170 (the return null path in findEnclosingClass for a top-level function). The null-class path is untested but harmless: if findEnclosingClass returns null, prefix defaults to '' and joinPath('', rawPath) returns /${rawPath}, which is correct behavior for a route annotation on a top-level function.

The 3 grammar-absent behaviors are tested implicitly: when tree-sitter-kotlin isn't installed, the entire plugin is null, REGISTRY skips the entries, and getPluginForFile('.kt') returns undefined — the orchestrator's existing if (!plugin) { return []; } guard covers this path. A dedicated grammar-absent integration test would strengthen coverage, but the skip gate + orchestrator guard is considered acceptable for an optional-dependency feature.

F. Packaging/install behavior

tree-sitter-kotlin: "^0.3.8" is correctly declared in gitnexus/package.json optionalDependencies (line 97). It was pre-existing before this PR (installed for the Kotlin scope resolver). The optionalDependencies placement means npm install will attempt native build but will not fail if the binary can't be compiled. CI installs it successfully (evidenced by the 54/0 test result). Packaged smoke tests (ubuntu + windows) both passed.

G. Regression risk to existing providers

✅ Zero regression. The changes to index.ts are additive only: one new import, one conditional if (KOTLIN_HTTP_PLUGIN) block, and kt,kts appended to the glob string. Java, Go, Python, PHP, JS/TS are unaffected. The HTTP_SCAN_GLOB change only adds extensions; all existing extensions remain present. http-route-extractor.ts is unchanged. The 43 pre-existing tests all passed.

H. Hygiene/security

✅ No hidden Unicode characters found in any of the 3 changed files. No bidirectional controls, zero-width spaces, or BOM markers. CodeQL passed. Gitleaks passed. Dependency Review passed. The createRequire(import.meta.url) pattern is consistent with how parse-worker.ts and parser-loader.ts handle optional native grammars in this codebase.


Back-and-forth avoided by verifying

  • tree-sitter-kotlin dependency: confirmed in optionalDependencies — no missing dependency issue.
  • Kotlin tests in CI: confirmed 54/0 pass/skip — not silently skipped.
  • Positional anchor semantics: confirmed correct by tree-sitter query spec analysis.
  • Both positional and named patterns compiled inside buildKotlinPlugin() — no module-load crash when grammar absent.
  • PR Autofix: confirmed it is not advisory-only — it corresponds to a failing quality / format required check.

Open questions that remain only if unavoidable

None — all mandatory verification points were resolved.


Final verdict

❌ not production-ready

One merge blocker: quality / format fails because gitnexus/test/unit/group/http-route-extractor.test.ts does not pass npx prettier --check .. The fix is trivial (run /autofix or npx prettier --write on the test file), but DoD.md §6 is explicit that formatter bypass is a not-done signal.

Everything else is production-quality: the optional dependency is correctly declared, the tree-sitter query anchoring is correct, false-positive resistance is verified through both code analysis and anti-regression tests, the Kotlin tests actually ran and passed in CI (not silently skipped), path joining is identical to the Java plugin, the null-grammar fallback is safe, and there is zero regression risk to existing language plugins.

Fix required: Comment /autofix on this PR (or run npx prettier --write gitnexus/test/unit/group/http-route-extractor.test.ts locally and push). After that, re-check quality / format — all other gates are already green and this PR can be approved.


| CI run (failing) | format job

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
9999 9995 0 4 610s

✅ All 9995 tests passed

4 test(s) skipped — expand for details
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 79.73% 34779/43617 79.48% 📈 +0.2 🟢 ███████████████░░░░░
Branches 68.25% 22170/32479 68% 📈 +0.2 🟢 █████████████░░░░░░░
Functions 84.67% 3602/4254 84.56% 📈 +0.1 🟢 ████████████████░░░░
Lines 83.22% 31325/37639 82.97% 📈 +0.2 🟢 ████████████████░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

@henry201605 could you please look into claude's findings above?

@henry201605

Copy link
Copy Markdown
Contributor Author

@magyargergo Thanks for flagging — and apologies, I should have run npm run format locally before pushing 🙏

Already addressed in commit 73e22fc8 (pushed ~30 min ago, before your message). It runs npx prettier --write on the test file — purely line-wrapping for two itKotlin('...') titles that exceeded the 100-char printWidth, no logic changes. The quality / format job now passes ✅ (run).

Looks like the only remaining red is the Vercel deploy authorization, same as on #1834. Let me know if there's anything else you'd like adjusted!

@magyargergo

Copy link
Copy Markdown
Collaborator

Claude finished @magyargergo's task in 5m 30s —— View job

Production-Readiness Review: PR #1849 — Kotlin Spring HTTP Route Extraction

Review complete.

Review bar for this PR

Three lenses: (1) GitNexus core maintainer — source-scan/group contract extraction, optional parser loading, test/CI wiring, DoD enforcement; (2) Kotlin expert — tree-sitter-kotlin AST behavior, annotation argument forms, optional native grammar; (3) Spring Boot expert — route annotation semantics, false-positive resistance, path joining.

Problem being solved

GitNexus Strategy B (source-scan) had no Kotlin Spring Boot support. PR #1834 (merged) fixed Java named-arg Spring route extraction (@RequestMapping(path = "/api")). This PR extends the same fix to Kotlin controllers, where the Kotlin grammar (fwcd/tree-sitter-kotlin) uses value_argument for both positional and named annotation args — requiring a split-query approach analogous to what Java now uses for element_value_pair.

Current PR state

  • Branch: henry201605:feat/kotlin-spring-http-routesabhigyanpatwari:main
  • Commits: 1 clean feature commit (ec50581)
  • Changed files: 3 (all domain-consistent: http-patterns/kotlin.ts new, http-patterns/index.ts +17, test file +313)
  • CI overall: FAILED — 1 job failing, 8 passing

Merge status and mergeability

❌ Not mergeablequality / format (job 78023475347) fails with:

[warn] gitnexus/test/unit/group/http-route-extractor.test.ts
[warn] Code style issues found in the above file. Run Prettier with --write to fix.
Process completed with exit code 1.

All other jobs pass: quality / lint ✅, quality / typecheck ✅, tests / ubuntu / coverage ✅, tests / windows-latest ✅, tests / macos-latest ✅, tests / packaged install smoke (ubuntu + windows) ✅, CodeQL ✅, Dependency Review ✅, Docker Build & Push ✅.

Repository history considered

Branch hygiene assessment

Clean feature PR. One commit, three domain-consistent files. No unrelated formatting churn in unchanged sections. No hidden merge-from-main commits. No package.json/lockfile drift needed (dependency was already declared).

The Prettier failure is in new code only (http-route-extractor.test.ts lines added by this PR).

Understanding of the change

kotlin.ts is a new HttpLanguagePlugin that:

  1. Loads tree-sitter-kotlin via createRequire inside a try/catch; exports null on failure
  2. Inside buildKotlinPlugin() — called only when grammar is non-null — compiles two pattern bundles: class-prefix patterns (2 compiled queries) and method-route patterns (2 compiled queries)
  3. The scan() method builds a prefixByClassId map from class matches, then for each method match walks findEnclosingClass, looks up the prefix, calls joinPath, and pushes an HttpDetection with role: 'provider', framework: 'spring', confidence: 0.8

index.ts conditionally registers .kt/.kts in the REGISTRY only when KOTLIN_HTTP_PLUGIN !== null, and adds kt,kts to HTTP_SCAN_GLOB unconditionally (with a comment explaining that getPluginForFile gracefully returns undefined when grammar is absent).

Findings

Finding 1: quality / format CI failure — test file fails Prettier check

Risk: MERGE BLOCKER

Evidence found: CI job 78023475347 (quality / format) fails at step "Run npx prettier --check .":

[warn] gitnexus/test/unit/group/http-route-extractor.test.ts
[warn] Code style issues found in the above file. Run Prettier with --write to fix.

The PR Autofix bot (run 26495758078) already identified 94 changed lines with fixable issues. DoD.md §6 and §4.2 are explicit: "prettier was bypassed without explicit justification" is a not-done signal and npx prettier --check . is part of the validation baseline.

Recommended fix: Run /autofix on the PR, or locally cd gitnexus && npx prettier --write test/unit/group/http-route-extractor.test.ts and push. This is a one-command fix.

Blocks merge: yes

PR-specific assessment sections

A. Runtime registration and optional grammar safety

✅ Confirmed safe. kotlin.ts exports KOTLIN_HTTP_PLUGIN: HttpLanguagePlugin | null. index.ts only registers .kt/.kts in REGISTRY when KOTLIN_HTTP_PLUGIN is truthy (index.ts:39-42). http-route-extractor.ts:163-169 calls getPluginForFile(rel) and returns [] immediately when !plugin — no crash path. HTTP_SCAN_GLOB includes kt,kts unconditionally, but this only adds files to the glob result; each file's plugin lookup is still guarded. Performance impact of scanning .kt files when grammar is absent is bounded by the getPluginForFileundefined early-return; no parsing occurs.

compilePatterns is called inside buildKotlinPlugin() (not at module-load-time like Java). This is the correct design: calling new Parser.Query(null, ...) would throw. Java can compile at module load because tree-sitter-java is a hard dependency; Kotlin cannot because the binding is optional.

B. Kotlin AST/query correctness

✅ Confirmed correct for the claimed scope.

Positional anchor ((value_argument . (string_literal) @prefix)): The . anchor in tree-sitter query syntax means the following node must be the first named child of its parent. For a positional arg @GetMapping("/users"), value_argument has one named child: string_literal → first named child → matches. For a named arg @GetMapping(path = "/users"), value_argument has simple_identifier as its first named child, followed by string_literalstring_literal is NOT first → does not match. The anchor correctly excludes named forms from the positional query.

Named query key constraint (#match? @key "^(path|value)$"): Directly mirrors the Java plugin's #match? @key "^(path|value)$" on element_value_pair. Confirmed at kotlin.ts:113 and kotlin.ts:152.

No double-match: For any given @RequestMapping(path = "/api"), only the named pattern fires (positional anchor fails because simple_identifier precedes string_literal). For @RequestMapping("/api"), only the positional pattern fires (no simple_identifier in value_argument). One match per annotation argument. prefixByClassId.set is called once per class. No overwrite risk.

findEnclosingClass: Identical semantics to java.ts (kotlin.ts:164-171 vs java.ts:196-203). Uses 'class_declaration' type — correct for Kotlin. Note: Kotlin object declarations use object_declaration in the grammar, so singleton Spring controllers would not have their class-level prefix resolved. This is an out-of-scope edge case for typical Spring Boot usage and is acceptable as a known limitation.

joinPath: Line-for-line identical to java.ts:211-216. Verified at kotlin.ts:179-184.

C. Spring Boot route semantics

✅ Scope is correctly limited and does not overclaim. The PR description explicitly lists what is supported (class positional + named prefix, method positional + named for Get/Post/Put/Delete/PatchMapping). Intentionally excluded: array-value paths (value = ["/a", "/b"]), @RequestMapping on methods (only class-level prefix is extracted), fully-qualified annotations (org.springframework.web.bind.annotation.GetMapping), alias imports, composed annotations, consumer patterns. The PR description is accurate and honest about scope.

D. False-positive/false-negative behavior

✅ Confirmed correct via direct query analysis and confirmed by anti-regression tests.

For @GetMapping(produces = "application/json"):

  • Positional query: produces = ...simple_identifier is first named child of value_argument → positional query does NOT match ✓
  • Named query: key is "produces"#match? @key "^(path|value)$" fails → does NOT match ✓
  • Result: zero routes emitted ✓ (test at test.ts:729-762)

For @GetMapping(name = "listUsers", value = "/users"):

  • value_argument(name = "listUsers"): key is "name" → fails key constraint ✓
  • value_argument(value = "/users"): key is "value" → passes key constraint → one route at /users
  • Result: exactly one route http::GET::/users (test at test.ts:764-799)

For @RequestMapping(path = "/api", name = "myApi"):

  • Named query: path argument matches, prefixByClassId gets /api. name argument fails key constraint.
  • Result: prefix is /api, not myApi (test at test.ts:801-827)

E. Test coverage and CI reality

✅ Tests actually ran in CI — not silently skipped.

The ubuntu/coverage log confirms: test/unit/group/http-route-extractor.test.ts ran 54 tests, 0 skipped in 300ms. Since the pre-PR count was 43 tests (54 − 11 new Kotlin tests), all 11 Kotlin cases executed. tree-sitter-kotlin: ^0.3.8 in optionalDependencies built successfully on ubuntu-latest.

Coverage for http-patterns/kotlin.ts:

kotlin.ts | 87.75 | 70.37 | 100 | 95.23 | 49,170

Uncovered lines: 49 (the Kotlin = null catch branch — expected when grammar is available) and 170 (the return null path in findEnclosingClass for a top-level function). The null-class path is untested but harmless: if findEnclosingClass returns null, prefix defaults to '' and joinPath('', rawPath) returns /${rawPath}, which is correct behavior for a route annotation on a top-level function.

The 3 grammar-absent behaviors are tested implicitly: when tree-sitter-kotlin isn't installed, the entire plugin is null, REGISTRY skips the entries, and getPluginForFile('.kt') returns undefined — the orchestrator's existing if (!plugin) { return []; } guard covers this path. A dedicated grammar-absent integration test would strengthen coverage, but the skip gate + orchestrator guard is considered acceptable for an optional-dependency feature.

F. Packaging/install behavior

tree-sitter-kotlin: "^0.3.8" is correctly declared in gitnexus/package.json optionalDependencies (line 97). It was pre-existing before this PR (installed for the Kotlin scope resolver). The optionalDependencies placement means npm install will attempt native build but will not fail if the binary can't be compiled. CI installs it successfully (evidenced by the 54/0 test result). Packaged smoke tests (ubuntu + windows) both passed.

G. Regression risk to existing providers

✅ Zero regression. The changes to index.ts are additive only: one new import, one conditional if (KOTLIN_HTTP_PLUGIN) block, and kt,kts appended to the glob string. Java, Go, Python, PHP, JS/TS are unaffected. The HTTP_SCAN_GLOB change only adds extensions; all existing extensions remain present. http-route-extractor.ts is unchanged. The 43 pre-existing tests all passed.

H. Hygiene/security

✅ No hidden Unicode characters found in any of the 3 changed files. No bidirectional controls, zero-width spaces, or BOM markers. CodeQL passed. Gitleaks passed. Dependency Review passed. The createRequire(import.meta.url) pattern is consistent with how parse-worker.ts and parser-loader.ts handle optional native grammars in this codebase.

Back-and-forth avoided by verifying

  • tree-sitter-kotlin dependency: confirmed in optionalDependencies — no missing dependency issue.
  • Kotlin tests in CI: confirmed 54/0 pass/skip — not silently skipped.
  • Positional anchor semantics: confirmed correct by tree-sitter query spec analysis.
  • Both positional and named patterns compiled inside buildKotlinPlugin() — no module-load crash when grammar absent.
  • PR Autofix: confirmed it is not advisory-only — it corresponds to a failing quality / format required check.

Open questions that remain only if unavoidable

None — all mandatory verification points were resolved.

Final verdict

❌ not production-ready

One merge blocker: quality / format fails because gitnexus/test/unit/group/http-route-extractor.test.ts does not pass npx prettier --check .. The fix is trivial (run /autofix or npx prettier --write on the test file), but DoD.md §6 is explicit that formatter bypass is a not-done signal.

Everything else is production-quality: the optional dependency is correctly declared, the tree-sitter query anchoring is correct, false-positive resistance is verified through both code analysis and anti-regression tests, the Kotlin tests actually ran and passed in CI (not silently skipped), path joining is identical to the Java plugin, the null-grammar fallback is safe, and there is zero regression risk to existing language plugins.

Fix required: Comment /autofix on this PR (or run npx prettier --write gitnexus/test/unit/group/http-route-extractor.test.ts locally and push). After that, re-check quality / format — all other gates are already green and this PR can be approved.

| CI run (failing) | format job

Sorry I meant these findings

@henry201605

Copy link
Copy Markdown
Contributor Author

No worries at all! 🙏 My reply just above (commit 73e22fc8) addresses exactly that finding — quality / format is green now (job run). All other CI gates already passed per the Claude review. Happy to address anything else if needed!

@magyargergo magyargergo merged commit 46eb0eb into abhigyanpatwari:main May 27, 2026
25 of 26 checks passed
henry201605 pushed a commit to henry201605/GitNexus that referenced this pull request May 27, 2026
Address Claude review on PR abhigyanpatwari#1855 (Finding 1).

The OkHttp query in `kotlin.ts:OK_HTTP_PATTERNS` matches the
`.url("/x")` sub-expression of a builder chain, but the verb is
encoded on a separate sibling call (`.post(body)` / `.delete()` /
...). The query intentionally does not walk the chain to recover
the verb — it emits `method: 'GET'` for every match, mirroring the
Java plugin's `OK_HTTP_PATTERNS` (java.ts).

Concretely: `Request.Builder().url("/x").post(body).build()` becomes
`http::GET::/x`, not `http::POST::/x`. This is an already-accepted
Java parity heuristic, but it was untested on the Kotlin side.

This commit:
  - Adds an anti-overreach test pinning the current behavior:
      * exactly one consumer is emitted with method=GET
      * no second http::POST::/x consumer appears
  - Documents the limitation in kotlin.ts as a "Known limitation"
    block tied to the test, so a future verb-walk implementation
    has to update the comment in lockstep with the assertion.

Rationale for not implementing verb-walk in this PR:
  - Verb-walk requires walking sibling call_expression nodes (the
    `.post(body)` chain), which is the same shape as the
    deferred WebClient long-form work
  - Java has the same limitation in production today; fixing only
    Kotlin would create polyglot drift
  - A coordinated future PR can add verb-walk to both plugins at
    once and update both comments + the pin tests together

Finding 2 (silent test-skip when tree-sitter-kotlin grammar is
unavailable) is intentionally NOT addressed here — same gating
pattern was accepted in abhigyanpatwari#1849 for Provider tests, and a coordinated
follow-up should add a CI sentinel covering both Provider and
Consumer suites in one place.

Local validation:
  - test/unit/group/http-route-extractor.test.ts: 60/60 ✅
  - test/unit/group: 540/540 ✅
  - npm run format:check: clean ✅
magyargergo pushed a commit that referenced this pull request May 27, 2026
* feat(group): add Kotlin Spring HTTP consumer extraction

Follow-up to #1849 (Kotlin providers). Extends `http-patterns/kotlin.ts`
with three call-site patterns common in Kotlin Spring projects:

  - RestTemplate: `restTemplate.getForObject("/x", ...)` and the
    full verb family (getForObject/getForEntity → GET,
    postForObject/postForEntity → POST, put → PUT, delete → DELETE,
    patchForObject → PATCH). Mirrors the Java plugin's
    `REST_TEMPLATE_TO_HTTP` map so polyglot repos coalesce on a
    single contract id.

  - WebClient short form: `webClient.get().uri("/x")` and the
    `.post()` / `.put()` / `.delete()` / `.patch()` siblings. The
    chain parses as two nested `call_expression` nodes; the query
    anchors on the outer `.uri(...)` and walks one level inward
    to constrain the verb.

  - OkHttp: `Request.Builder().url("/x")`. Kotlin parses
    `Request.Builder()` as a `call_expression` whose callee is a
    `navigation_expression` (not Java's `object_creation_expression`),
    so the query shape differs from `java.ts` but the receiver/method
    constraints (`Request` / `Builder` / `url`) and emitted
    contract format match.

Out of scope: `webClient.method(HttpMethod.X).uri("/y")` long form.
The verb sits on a sibling `call_expression` two hops away, so it
needs a walk-up helper rather than a flat tree-sitter query. A
dedicated anti-overreach test pins the current behavior so a future
short-form change can't accidentally start matching the long form.

Receiver name constraints (`#eq? @obj "restTemplate"`,
`#eq? @cls "Request"`) match the Java plugin's heuristic — a project
that aliases the receiver under a different name won't be picked up.
This trade-off keeps false-positive rates low and is documented in
the file header.

Tests: 5 new cases under `consumer extraction — fetch patterns`,
gated by tree-sitter-kotlin grammar availability.

  positive (3)
   - RestTemplate verbs (5 calls × 5 verbs)
   - WebClient short-form verbs (5 calls × 5 verbs)
   - OkHttp Request.Builder().url("/x")
  anti-regression (2)
   - WebClient long form `.method(HttpMethod.X)` produces no
     consumer (deferred-feature pin)
   - non-restTemplate receiver does not match (receiver-name pin)

Reverse-validated: removing the `(#eq? @obj "restTemplate")`
constraint causes the receiver-name anti-regression test to fail.

Local validation:
  - test/unit/group/http-route-extractor.test.ts: 59/59 ✅
  - test/unit/group: 539/539 ✅
  - npm run format:check: clean ✅

* test(group): pin Kotlin OkHttp POST-chain heuristic-default GET behavior

Address Claude review on PR #1855 (Finding 1).

The OkHttp query in `kotlin.ts:OK_HTTP_PATTERNS` matches the
`.url("/x")` sub-expression of a builder chain, but the verb is
encoded on a separate sibling call (`.post(body)` / `.delete()` /
...). The query intentionally does not walk the chain to recover
the verb — it emits `method: 'GET'` for every match, mirroring the
Java plugin's `OK_HTTP_PATTERNS` (java.ts).

Concretely: `Request.Builder().url("/x").post(body).build()` becomes
`http::GET::/x`, not `http::POST::/x`. This is an already-accepted
Java parity heuristic, but it was untested on the Kotlin side.

This commit:
  - Adds an anti-overreach test pinning the current behavior:
      * exactly one consumer is emitted with method=GET
      * no second http::POST::/x consumer appears
  - Documents the limitation in kotlin.ts as a "Known limitation"
    block tied to the test, so a future verb-walk implementation
    has to update the comment in lockstep with the assertion.

Rationale for not implementing verb-walk in this PR:
  - Verb-walk requires walking sibling call_expression nodes (the
    `.post(body)` chain), which is the same shape as the
    deferred WebClient long-form work
  - Java has the same limitation in production today; fixing only
    Kotlin would create polyglot drift
  - A coordinated future PR can add verb-walk to both plugins at
    once and update both comments + the pin tests together

Finding 2 (silent test-skip when tree-sitter-kotlin grammar is
unavailable) is intentionally NOT addressed here — same gating
pattern was accepted in #1849 for Provider tests, and a coordinated
follow-up should add a CI sentinel covering both Provider and
Consumer suites in one place.

Local validation:
  - test/unit/group/http-route-extractor.test.ts: 60/60 ✅
  - test/unit/group: 540/540 ✅
  - npm run format:check: clean ✅

---------

Co-authored-by: henry <zhangwei2017@unipus.cn>
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.

2 participants