propose: @FeignClient is a caller, not an exposer#25
Merged
Conversation
Reported-by: review on cross_service_smoke A @FeignClient method is a client — it makes outbound HTTP calls. But the indexer emits an EXPOSES edge from every Feign method to a consumer-side Route node, treating the caller as if it also exposed the endpoint. The same wire ends up represented as two unrelated Route nodes (one consumer-side in the caller, one endpoint-side in the server) and the HTTP_CALLS edge lands on the wrong one, producing intra_service match labels for what are really cross-service calls. The root cause: ast_java.py:2178 already distinguishes kind=http_consumer (Feign) from kind=http_endpoint (Controller), but build_ast_graph.py:1354-1364 emits EXPOSES unconditionally per RouteDecl. The semantic distinction was made; it's just not honoured at emission. Single-PR fix (~150 LOC): suppress EXPOSES emission when kind=http_consumer, extend the cross-service matcher to pair consumer Routes against endpoint Routes across microservices, add pass4_feign_exposes_suppressed counter to graph_meta with new _META_PR_FEIGN tier. Status: draft. Six [TBD] items including a recommendation to audit ASYNC_CALLS for the symmetric bug before implementation.
Audited all five RouteDecl emission sites in ast_java.py for the symmetric Feign-style EXPOSES bug. Verdict: bug does not exist on the async side. Async uses structurally different annotations per direction (@KafkaListener consumer vs KafkaTemplate.send producer); only listeners emit RouteDecl, producers emit OutgoingCall. Verified empirically on cross_service_smoke fixture. Fix stays HTTP-only.
HumanBean17
added a commit
that referenced
this pull request
May 5, 2026
…#27) Implements the merged proposes (#25 #26) as per-PR plans following the PLAN-POST-TIER1B-FOLLOWUPS structure: scope, out-of-scope, background, failure modes, resolution with code anchors, tests table, manual evidence, definition of done, risk register, followups, references. PLAN-FEIGN-NOT-AN-EXPOSER: single PR (PR-F1), gates EXPOSES emission on route.kind != 'http_consumer', surfaces suppression count in GraphMeta. No schema bump (additive nullable column). PLAN-CROSS-SERVICE-RESOLUTION-FLAG: single PR (PR-G1), adds cross_service_resolution flag (auto | brownfield_only) to .lancedb-mcp.yml. Gates pass6_match_edges cross-service candidates on _is_brownfield_sourced helper. Ontology bump 7->8.
This was referenced May 6, 2026
HumanBean17
added a commit
that referenced
this pull request
May 6, 2026
* chore: tidy completed plans/proposes and refresh stale docs Move completed plans to plans/completed/: - PLAN-CLIENT-ROLE-RENAME.md (PR #33 merged) - PLAN-CROSS-SERVICE-RESOLUTION-FLAG.md (PR #30 merged) - PLAN-FEIGN-NOT-AN-EXPOSER.md (PR #31 merged) Move completed proposes to propose/completed/: - CLIENT-ROLE-RENAME-PROPOSE.md (PR #28 merged) - CROSS-SERVICE-RESOLUTION-FLAG-PROPOSE.md (PR #26 merged) - FEIGN-NOT-AN-EXPOSER-PROPOSE.md (PR #25 merged) Refresh active docs: - README.md "Deferred" section: trace_request_flow, find_route_callers, HTTP_CALLS/ASYNC_CALLS are shipped (not deferred). Add explicit pointers to the still-active TIER2-INCREMENTAL-REBUILD and REFRESH-CODE-INDEX-AUTO-MODE proposes for the incremental Kuzu work. - CODEBASE_REQUIREMENTS.md A.3: drop the stale 'ontology version 3' literal (now 9) and fix references to PLAN-CAPABILITIES-MODEL and CALL-GRAPH-PROPOSE to use their completed/ paths. Tense matches reality (call-graph layer is shipped, not deferred). - CODEBASE_REQUIREMENTS.md B.9: same fix for the propose/DEFERRED-CALL-GRAPH-PROPOSE.md reference; the propose lives under propose/completed/CALL-GRAPH-PROPOSE.md. No code changes. Test baseline unchanged: 290 passed, 4 skipped. * docs: add inline Java stubs for @CodebaseRoute / @CodebaseClient / @CodebaseProducer Per pushback on PR #34: the route, client, and producer brownfield annotations were mentioned 4x in README + CODEBASE_REQUIREMENTS but their @interface stubs were never shown inline. Users had to spelunk through tests/fixtures/ to know what to copy into their project. README §5 'Brownfield overrides — Last resort — source stubs' now has three explicit subsections: - 3a. Roles & capabilities — @CodebaseRole / @CodebaseCapability / @CodebaseCapabilities (class-level), with usage example. - 3b. Routes — @CodebaseRoute / @CodebaseRoutes + CodebaseRouteFrameworkKind / CodebaseRouteKind (method-level), with HTTP-endpoint and Kafka-consumer usage examples. - 3c. Clients & producers — @CodebaseClient / @CodebaseClients and @CodebaseProducer / @CodebaseProducers (method-level), with rest_template + kafka_send usage examples. Stub Java in the doc matches the verbatim sources under tests/fixtures/brownfield_route_stubs/ and brownfield_client_stubs/ (also referenced for copy-paste). Enum values mirror VALID_ROUTE_* and VALID_CLIENT_KINDS in java_ontology.py. CODEBASE_REQUIREMENTS.md A.2.1 updated to enumerate all three annotation families (roles, routes, clients/producers) and link to the matching README sections instead of only mentioning role stubs. No code change. Test baseline unchanged: 290 passed, 4 skipped.
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.
What
Adds
propose/FEIGN-NOT-AN-EXPOSER-PROPOSE.md— a draft proposal to fix the design issue where@FeignClientmethods (HTTP callers) are emitted as if they also exposed the endpoints they call.TL;DR for the design issue
Verified on
tests/fixtures/cross_service_smoke:So one HTTP wire produces:
intra_serviceHow the bug exists despite the annotation literally saying "client"
Not a typo — a design assumption that didn't generalise: "any method with a
@*Mappingannotation declares a Route, and any method declaring a Route gets an EXPOSES edge."ast_java.py:2178already correctly distinguishes the two cases:But
build_ast_graph.py:1354-1364emits EXPOSES unconditionally perRouteDecl, regardless ofkind. The semantic distinction was made; it's just not honoured at emission. One conditional fixes it.What's in the proposal
and decl.kind != "http_consumer") plus matcher extension to pair consumer Routes against endpoint RoutesWhat's NOT in the proposal
FEIGN_CLIENTrole — separate larger discussion (§8 Future work)HTTP_CLIENTas a generalised roleStatus
Draft — open for review. The user wants this queued; not implementing now. After review and §9 [TBD] resolution, an implementable plan derives at
plans/PLAN-FEIGN-NOT-AN-EXPOSER.md.Sibling proposal: #24 (shared-module multi-attribution). Both touch the cross-service edge story but on independent axes — multi-attribution fixes shared-DTO false-positives in CALLS/EXTENDS/IMPLEMENTS/INJECTS; this proposal fixes wrong-direction edges in EXPOSES/HTTP_CALLS.