Skip to content

fix(extract): emit HTTP_CALLS for Retrofit interface methods (#2723) - #2905

Open
divakantpandey wants to merge 3 commits into
DeusData:mainfrom
divakantpandey:fix/2723-retrofit-client-http-calls
Open

divakantpandey wants to merge 3 commits into
DeusData:mainfrom
divakantpandey:fix/2723-retrofit-client-http-calls

Conversation

@divakantpandey

@divakantpandey divakantpandey commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2723.

Retrofit's @GET/@POST/... match the bare-verb branch of annotation_route_method that exists for JAX-RS, so each interface method became a Route with a HANDLES edge in the client project. Non-@Path paths must start with /, so Retrofit's relative v1/orders fell back to /.

JAX-RS verbs are marker annotations (the path is a sibling @Path), and Retrofit services must be interfaces, so a verb annotation with an argument on a method declared in a Java or Kotlin interface is a Retrofit request. Controller classes that put the path in @GET("/x") (Pippo, Rapidoid) keep their HANDLES. Such methods now store client_route_path instead of route_path, and cbm_pipeline_emit_def_route emits HTTP_CALLS with the same url_path/method properties as call-site service edges, so cross-repo matching treats them like any other outbound call. A separate field keeps every route_path consumer (prefix bridge, decorator backstop, UI) meaning "served endpoint" unchanged. The incremental routes re-extract an edited file and go through the same emit, so ensure_decorator_routes needs no change; the incremental test covers both routes.

Path handling: a leading / is kept, a relative path is rooted (Retrofit resolves it against the base URL), and a full URL is kept as written. A const val, a string template or a concatenation has no static value: no edge, rather than a / Route. Resolving same-file constants would cover the remaining two cases in the issue; I left it out to keep this change to the direction fix.

On the issue's fixture: client HANDLES 4 -> 0, HTTP_CALLS 0 -> 2; client -> server cross_http_calls 0 -> 2; billing caller 2 -> 1 (no longer lands on the client). JAX-RS server routes unchanged.

Checklist

  • Every commit is signed off
  • Tests pass locally: scripts/test.sh --suites pipeline_semantic_manifest_repro,pipeline,extraction,edge_types_probe,cross_repo,incremental (1376 passed, ASan/UBSan, macOS arm64); full scripts/test.sh: 10324 passed, 0 failed
  • Lint: clang-format 20 clean on changed sources; cppcheck/clang-tidy not run locally
  • New behavior is covered by a test: Kotlin and Java Retrofit (sequential + parallel), a JAX-RS interface control, a Java/Kotlin controller-class control that keeps HANDLES, and an incremental re-index; the Retrofit tests fail without the fix and the controller-class test fails without the interface gate

…a#2723)

Retrofit's @GET/@POST/... share their names with the JAX-RS verbs, so
every annotated client method became a served Route with a HANDLES edge,
and any path without a leading slash collapsed onto "/". JAX-RS verbs are
marker annotations; a verb that carries an argument is a Retrofit request.
Record its path as client_route_path and emit HTTP_CALLS to the Route
instead. Relative paths are rooted, full URLs kept; constants, templates
and concatenations have no static value and produce no edge.

Signed-off-by: Divakant Pandey <divakant01@gmail.com>
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

@DeusData

DeusData commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Thank you so much for this, @divakantpandey! This is a precise fix for a real graph-quality problem: a Retrofit interface describes calls the client makes, and the graph was turning each one into a served endpoint. Several things here are exactly right:

  • The discriminator is well chosen. JAX-RS verbs are marker annotations with the path in a sibling @Path, so a bare verb that carries an argument is a Retrofit request. Your JAX-RS interface control test pins that the server side keeps its HANDLES.
  • Reusing HTTP_CALLS with the same url_path / method properties that call-site edges carry means cross-repo matching treats these methods like any other outbound call, with no new edge type. Keeping a separate field, so every route_path consumer still means "served endpoint", is a careful choice.
  • No / collapse any more: a relative path is rooted the way Retrofit resolves it against the base URL, a full URL is kept, and a constant or template with no static value yields no edge instead of a wrong one.
  • The tests cover Kotlin and Java, both the sequential and the parallel path, and both incremental routes, and the Retrofit cases fail without the fix.

One request before we merge:

  1. Could the Retrofit reading apply only to methods declared in an interface (a Java interface, or a Kotlin interface)? Retrofit requires service declarations to be interfaces, while some server frameworks, for example Pippo and Rapidoid, put the path inside @GET("/x") on a controller class. Today those endpoints get a Route and a HANDLES edge, and with this change they would turn into outbound calls. A test that indexes such a controller class and asserts it keeps HANDLES and gets no HTTP_CALLS would pin it. Nothing changes for any Retrofit interface.

Also, a production file this PR touches (src/pipeline/pipeline.c) changed on main after your CI run, so please merge the current main in with that change, so CI runs on the merged tree. There are no conflicts.

Thanks again for the careful work, and for leaving the same-file constant resolution for a separate change. Keeping this one focused on the direction fix made it easy to review.

…#2723)

Pippo and Rapidoid put the path in @get("/x") on a controller class, and
those methods serve the route. Retrofit services must be interfaces, so
limit the client reading to Java and Kotlin interfaces.

Signed-off-by: Divakant Pandey <divakant01@gmail.com>
…into fix/2723-retrofit-client-http-calls

Signed-off-by: Divakant Pandey <divakant01@gmail.com>
@divakantpandey

Copy link
Copy Markdown
Contributor Author

Limited the Retrofit reading to methods declared in a Java or Kotlin interface, and added a control test with a Pippo-style Java controller and a Rapidoid-style Kotlin class that keep HANDLES and get no HTTP_CALLS (it fails without the gate). Also merged current main; CI is green on the merged tree.

This branch has not been deployed

No deployments
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.

Kotlin/Java: Retrofit client interfaces are minted as server Route nodes instead of outbound HTTP_CALLS

2 participants