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 <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-06-28 02:04:36 +02:00
parent 04697c632c
commit beaa776ef1
2 changed files with 49 additions and 0 deletions
+24
View File
@@ -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') {
+25
View File
@@ -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,