@@ -3629,6 +3629,69 @@ TEST(trace_evidence_strategy_class_vocabulary_is_closed) {
36293629 PASS();
36303630}
36313631
3632+ /* A confidence the code cannot read must be reported as "not recorded", not
3633+ * as a recorded zero.
3634+ *
3635+ * The emitter reserves ev_conf < 0 for "no confidence on this edge" and
3636+ * prints "-" (text) or null (json). The reader set ev_conf with
3637+ * strtod(colon + 1, NULL), and strtod answers 0.0 for text it cannot read --
3638+ * so a malformed value passed the ev_conf >= 0.0 test and printed 0.00, the
3639+ * one value the surrounding code goes out of its way to keep meaningful.
3640+ * A caller then cannot tell "the resolver was certain this is wrong" from
3641+ * "nobody wrote a number here". */
3642+ TEST(tool_trace_path_unreadable_confidence_reports_not_recorded) {
3643+ cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
3644+ cbm_store_t *st = cbm_mcp_server_store(srv);
3645+ const char *proj = "badconf-proj";
3646+ cbm_mcp_server_set_project(srv, proj);
3647+ cbm_store_upsert_project(st, proj, "/tmp/badconf");
3648+ cbm_node_t caller = {.project = proj,
3649+ .label = "Function",
3650+ .name = "caller",
3651+ .qualified_name = "badconf-proj.src.caller",
3652+ .file_path = "src/a.c",
3653+ .start_line = 1,
3654+ .end_line = 5};
3655+ cbm_node_t callee = {.project = proj,
3656+ .label = "Function",
3657+ .name = "target",
3658+ .qualified_name = "badconf-proj.src.target",
3659+ .file_path = "src/a.c",
3660+ .start_line = 10,
3661+ .end_line = 20};
3662+ int64_t id_caller = cbm_store_upsert_node(st, &caller);
3663+ int64_t id_callee = cbm_store_upsert_node(st, &callee);
3664+ ASSERT_GT(id_caller, 0);
3665+ ASSERT_GT(id_callee, 0);
3666+ /* The strategy reads fine; only the confidence is malformed. */
3667+ cbm_edge_t e = {.project = proj,
3668+ .source_id = id_caller,
3669+ .target_id = id_callee,
3670+ .type = "CALLS",
3671+ .properties_json = "{\"callee\":\"target\",\"confidence\":null,"
3672+ "\"strategy\":\"lsp_trait_dispatch\",\"candidates\":1}"};
3673+ ASSERT_GT(cbm_store_insert_edge(st, &e), 0);
3674+
3675+ char *ev = cbm_mcp_server_handle(
3676+ srv, "{\"jsonrpc\":\"2.0\",\"id\":93,\"method\":\"tools/call\","
3677+ "\"params\":{\"name\":\"trace_path\",\"arguments\":{\"function_name\":\"caller\","
3678+ "\"project\":\"badconf-proj\",\"direction\":\"outbound\",\"include_evidence\":true}}}");
3679+ ASSERT_NOT_NULL(ev);
3680+ char *ev_txt = extract_text_content(ev);
3681+ ASSERT_NOT_NULL(ev_txt);
3682+ /* Positive controls: the hop and its readable class still come through, so
3683+ * a failure below is about the confidence and not a broken request. */
3684+ ASSERT_NOT_NULL(strstr(ev_txt, "target"));
3685+ ASSERT_NOT_NULL(strstr(ev_txt, "lsp"));
3686+ /* The claim: an unreadable confidence is never published as 0.00. */
3687+ ASSERT_NULL(strstr(ev_txt, "0.00"));
3688+ free(ev_txt);
3689+ free(ev);
3690+
3691+ cbm_mcp_server_free(srv);
3692+ PASS();
3693+ }
3694+
36323695/* Distilled from #559 (@vvenegasv). The indexer already records
36333696 * {strategy, confidence} on every CALLS edge (pass_calls.c:355) and the store
36343697 * reads it back, but no tool ever surfaced it — an agent could see THAT A->B
@@ -13972,6 +14035,7 @@ SUITE(mcp) {
1397214035 RUN_TEST(trace_evidence_strategy_class_vocabulary_is_closed);
1397314036 RUN_TEST(tool_trace_path_evidence_is_opt_in_and_class_mapped);
1397414037 RUN_TEST(tool_trace_path_evidence_columns_match_header_issue1542);
14038+ RUN_TEST(tool_trace_path_unreadable_confidence_reports_not_recorded);
1397514039 RUN_TEST(tool_trace_call_path_depth_clamped);
1397614040 RUN_TEST(tool_trace_call_path_distinct_defs_not_over_unioned);
1397714041 RUN_TEST(tool_trace_call_path_dts_stub_unions_with_impl);
0 commit comments