You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
pass_parallel.c emit_grpc_edge (around line 1216) emits Route nodes and GRPC_CALLS edges for any call site whose resolved QN looks gRPC-shaped, with two problems.
1. Wrong service name from greedy suffix-stripping
extract_grpc_service_method (line 1168) strips suffixes in this order:
ServiceClient is matched before Client. For a generated stub like FooServiceClient (Grpc.Tools convention), this strips to Foo — which does not match the .proto's declared service name FooService. The Route QN ends up __grpc__Foo/<rpc>, so cross-repo lookup against the consumer repo (which has the right name) misses.
2. False-positive Routes from local var names
When the resolved QN doesn't carry a generated-type suffix, the heuristic falls back to extracting the service name from the receiver var directly. In real codebases this picks up local fields like _provider, _builder, _someProvider, producing __grpc__provider/GetGroup, __grpc__builder/AddSomeService, etc. — Routes that look like gRPC services but correspond to no .proto anywhere. None match real services in target stores.
Suggested fixes
Preserve the canonical service name. The proto-declared service name is <X>Service; generated client class is <X>ServiceClient. Suffix list should strip only Client, Stub, BlockingStub, FutureStub, AsyncStub, AsyncClient, Servicer — not ServiceClient or ServiceGrpc.
Anchor on a stub-type signal, not a name shape. Only emit GRPC_CALLS when the receiver var has a typed assignment / ctor-param injection / DI registration that resolves to a known stub type — not when the resolved QN merely contains "Client" or "Stub" as a substring. The new pass_idl_scan introduced in feat: Tier 1 cross-repo gRPC matching + production-readiness fixes #293 takes this approach for its __route__grpc__ Routes (see Tier 1 detection rules) and includes a denylist for non-gRPC *Client types like System.Net.Http.HttpClient, RestSharp, Refit, Flurl, httpx. The same denylist + stub-suffix gate could be backported to pass_parallel.c directly.
Optional: align Route QN format.pass_parallel uses __grpc__<service>/<method>; HTTP/async/pass_idl_scan Routes use __route__<protocol>__<id>. Aligning to __route__grpc__<service>/<method> would let producer-side and consumer-side Routes converge into a single namespace, regardless of which pass emitted them. pass_idl_scan already uses the __route__grpc__ namespace.
This issue is separate from and not blocked by #293. The PR ships the full Tier 1 stack (producer + consumer detection) plus the production-readiness fixes documented in .planning/tier1-extractor-fixes.md, but pass_parallel.c emit_grpc_edge is not touched. The new pass_idl_scan uses a separate Route QN namespace (__route__grpc__) and stricter detection rules with a denylist, so the two passes coexist — but pass_parallel's phantom __grpc__ Routes still pollute the graph alongside the new pass's correct output.
The collision-warning mitigation in #293 (logged at idl_scan.route_collision, with service_qn stored on Route props for a future FQN-aware matcher) is for the new pass's bare-key collisions across .proto packages. None of that addresses pass_parallel's issue, which is structural — the suffix list and var-name fallback in extract_grpc_service_method need to change.
Problem
pass_parallel.c emit_grpc_edge(around line 1216) emitsRoutenodes andGRPC_CALLSedges for any call site whose resolved QN looks gRPC-shaped, with two problems.1. Wrong service name from greedy suffix-stripping
extract_grpc_service_method(line 1168) strips suffixes in this order:ServiceClientis matched beforeClient. For a generated stub likeFooServiceClient(Grpc.Tools convention), this strips toFoo— which does not match the.proto's declared service nameFooService. The Route QN ends up__grpc__Foo/<rpc>, so cross-repo lookup against the consumer repo (which has the right name) misses.2. False-positive Routes from local var names
When the resolved QN doesn't carry a generated-type suffix, the heuristic falls back to extracting the service name from the receiver var directly. In real codebases this picks up local fields like
_provider,_builder,_someProvider, producing__grpc__provider/GetGroup,__grpc__builder/AddSomeService, etc. — Routes that look like gRPC services but correspond to no.protoanywhere. None match real services in target stores.Suggested fixes
Preserve the canonical service name. The proto-declared service name is
<X>Service; generated client class is<X>ServiceClient. Suffix list should strip onlyClient,Stub,BlockingStub,FutureStub,AsyncStub,AsyncClient,Servicer— notServiceClientorServiceGrpc.Anchor on a stub-type signal, not a name shape. Only emit
GRPC_CALLSwhen the receiver var has a typed assignment / ctor-param injection / DI registration that resolves to a known stub type — not when the resolved QN merely contains "Client" or "Stub" as a substring. The newpass_idl_scanintroduced in feat: Tier 1 cross-repo gRPC matching + production-readiness fixes #293 takes this approach for its__route__grpc__Routes (see Tier 1 detection rules) and includes a denylist for non-gRPC*Clienttypes likeSystem.Net.Http.HttpClient,RestSharp,Refit,Flurl,httpx. The same denylist + stub-suffix gate could be backported topass_parallel.cdirectly.Optional: align Route QN format.
pass_paralleluses__grpc__<service>/<method>; HTTP/async/pass_idl_scanRoutes use__route__<protocol>__<id>. Aligning to__route__grpc__<service>/<method>would let producer-side and consumer-side Routes converge into a single namespace, regardless of which pass emitted them.pass_idl_scanalready uses the__route__grpc__namespace.Status / interaction with #293
This issue is separate from and not blocked by #293. The PR ships the full Tier 1 stack (producer + consumer detection) plus the production-readiness fixes documented in
.planning/tier1-extractor-fixes.md, butpass_parallel.c emit_grpc_edgeis not touched. The newpass_idl_scanuses a separate Route QN namespace (__route__grpc__) and stricter detection rules with a denylist, so the two passes coexist — butpass_parallel's phantom__grpc__Routes still pollute the graph alongside the new pass's correct output.The collision-warning mitigation in #293 (logged at
idl_scan.route_collision, withservice_qnstored on Route props for a future FQN-aware matcher) is for the new pass's bare-key collisions across.protopackages. None of that addressespass_parallel's issue, which is structural — the suffix list and var-name fallback inextract_grpc_service_methodneed to change.Cross-references
pass_idl_scanPR (full Tier 1 + production-readiness fixes)..planning/cbm-cross-repo-proposal.md(in feat: Tier 1 cross-repo gRPC matching + production-readiness fixes #293) — Tier 1g deferral rationale..planning/tier1-extractor-fixes.md(in feat: Tier 1 cross-repo gRPC matching + production-readiness fixes #293) — production-readiness gap log.