From beaa776ef1ec37a7c15f7a8f0d05045cc8bcae63 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sun, 28 Jun 2026 02:04:36 +0200 Subject: [PATCH] fix(pipeline): emit HTTP_CALLS for external client calls masked by registry mis-resolution (#523) A client call to an external HTTP library (`requests.get("/api/orders/{id}")`) carries its service-pattern signal in the callee_name. But the registry mis-resolves `requests.get` to a spurious builtin short-name match (`builtins.dict.get` via "get", strategy unique_name) -- a non-empty, non-HTTP QN. Both the empty-resolution fallback (checks the callee) and the resolved-QN service check (checks the resolved QN) therefore miss it, the call is dropped, no HTTP_CALLS edge is written, and cross-repo matching finds nothing to link. Detect the HTTP/ASYNC service pattern on the callee_name FIRST (before the registry can mask it) and emit the edge to a synthesized route node. Applied to both pipeline paths (pass_calls.c sequential + pass_parallel.c resolve_file_calls). Cross-repo HTTP linking now works for external client libraries whose source is not indexed. Signed-off-by: Martin Vogel --- src/pipeline/pass_calls.c | 24 ++++++++++++++++++++++++ src/pipeline/pass_parallel.c | 25 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/pipeline/pass_calls.c b/src/pipeline/pass_calls.c index c7c44c998..2e27adc18 100644 --- a/src/pipeline/pass_calls.c +++ b/src/pipeline/pass_calls.c @@ -445,6 +445,30 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call, } } + /* Service-pattern HTTP/ASYNC client call (`requests.get(url)`): the service + * signal lives in the callee_name. The registry can mis-resolve such a call + * to a spurious builtin short-name match (e.g. `requests.get` -> + * `builtins.dict.get` via "get", strategy unique_name), which is non-empty + * and not an HTTP pattern, so BOTH the empty-resolution and resolved-QN + * service checks below miss it and the call is dropped. Detect it on the + * callee_name FIRST so the HTTP_CALLS/ASYNC_CALLS edge is emitted regardless + * (target is a synthesized route node, not the unindexed library). (#523) */ + cbm_svc_kind_t csvc = cbm_service_pattern_match(call->callee_name); + if (csvc == CBM_SVC_HTTP || csvc == CBM_SVC_ASYNC) { + const char *cu = call->first_string_arg; + bool chas_url = cu && cu[0] != '\0' && + (cu[0] == '/' || strstr(cu, "://") != NULL || + (csvc == CBM_SVC_ASYNC && strlen(cu) > PAIR_LEN)); + if (chas_url) { + cbm_resolution_t svc_res = {.qualified_name = call->callee_name, + .confidence = PC_SVC_PATTERN_CONF, + .strategy = "service_pattern", + .candidate_count = 0}; + emit_http_async_edge(ctx, call, source_node, NULL, &svc_res, csvc); + return SKIP_ONE; + } + } + cbm_resolution_t res = cbm_registry_resolve(ctx->registry, call->callee_name, module_qn, imp_keys, imp_vals, imp_count); if (!res.qualified_name || res.qualified_name[0] == '\0') { diff --git a/src/pipeline/pass_parallel.c b/src/pipeline/pass_parallel.c index 5a34e8ef8..fefcf736a 100644 --- a/src/pipeline/pass_parallel.c +++ b/src/pipeline/pass_parallel.c @@ -1863,6 +1863,31 @@ static void resolve_file_calls(resolve_ctx_t *rc, resolve_worker_state_t *ws, CB continue; } + /* Service-pattern HTTP/ASYNC client call (`requests.get(url)`): the + * service signal lives in the callee_name. The registry can mis-resolve + * it to a spurious builtin short-name match (`requests.get` -> + * `builtins.dict.get` via "get"), which is non-empty and not an HTTP + * pattern, so the resolved-QN service checks below miss it and the call + * is dropped. Detect it on the callee_name FIRST so the HTTP_CALLS/ + * ASYNC_CALLS edge is emitted regardless (target is a synthesized route + * node, not the unindexed library). Mirrors pass_calls.c. (#523) */ + cbm_svc_kind_t csvc = cbm_service_pattern_match(call->callee_name); + if (csvc == CBM_SVC_HTTP || csvc == CBM_SVC_ASYNC) { + const char *cu = call->first_string_arg; + bool chas_url = cu && cu[0] != '\0' && + (cu[0] == '/' || strstr(cu, "://") != NULL || + (csvc == CBM_SVC_ASYNC && strlen(cu) > PP_ESC_SPACE)); + if (chas_url) { + cbm_resolution_t svc_res = {.qualified_name = call->callee_name, + .confidence = PP_HALF_CONF, + .strategy = "service_pattern"}; + emit_service_edge(ws->local_edge_buf, source_node, source_node, call, &svc_res, + module_qn, rc->registry, rc->main_gbuf, imp_keys, imp_vals, + imp_count); + continue; + } + } + if (!res.qualified_name || res.qualified_name[0] == '\0') { if (cbm_service_pattern_route_method(call->callee_name) != NULL) { cbm_resolution_t fake_res = {.qualified_name = call->callee_name,