From b0f48dbe178cf166bfd9e0bc8b10533aa7f464ed Mon Sep 17 00:00:00 2001 From: Kadir Mert Okumus Date: Sat, 24 Oct 2026 02:48:06 +0300 Subject: [PATCH 1/3] Share the ranking skeleton of the SCIP impact and implements maps ImpactMap::query and ImplementsMap::query repeated the same skeleton: per-seed edges, the drop count, the global cap, dedupe by destination, truncation to the request limit, the Partial note, the cost, and the hit and evidence construction. One function next to cap_edges in edge.rs, ranked_hits, now holds it; each map passes its descriptor, cap, note noun, edge source and evidence detail. The agent-facing note and detail strings are passed in verbatim. Evidence: cargo test -p lci-maps --lib scip_ 65 passed, 1 ignored, before and after; traverse_matrix 18/18 (goldens byte-identical); clippy --workspace --all-targets -D warnings clean; fmt clean. Co-Authored-By: Claude Opus 5.5 --- lci-maps/src/edge.rs | 67 ++++++++++++++++++++++++++- lci-maps/src/scip_impact.rs | 62 ++++++------------------- lci-maps/src/scip_impact/tests.rs | 2 +- lci-maps/src/scip_implements.rs | 62 ++++++------------------- lci-maps/src/scip_implements/tests.rs | 2 +- 5 files changed, 95 insertions(+), 100 deletions(-) diff --git a/lci-maps/src/edge.rs b/lci-maps/src/edge.rs index 79c5298f..9538adb8 100644 --- a/lci-maps/src/edge.rs +++ b/lci-maps/src/edge.rs @@ -7,10 +7,15 @@ //! rule 1): a consumer that wants a different order computes it at walk time. use crate::{ - Cost, EdgeKind, MapDescriptor, MapId, MapResponse, MapStatus, NodeKind, Observed, SourceLabel, + Cost, EdgeKind, Evidence, MapDescriptor, MapHit, MapId, MapRequest, MapResponse, MapStatus, + NodeKind, Observed, SnapshotView, SourceLabel, }; use lci_core::node::NodeId; -use std::{cmp::Reverse, time::Duration}; +use std::{ + cmp::Reverse, + collections::HashSet, + time::{Duration, Instant}, +}; /// How far a witness can be trusted, from the strongest to the weakest /// source. Consumers read `Compiler` and `Exact` by default and report @@ -232,6 +237,64 @@ pub fn cap_edges(edges: &mut Vec, cap: usize) { edges.truncate(cap); } +/// The seeded answer of a map whose edges carry a SCIP site: every seed's +/// capped edges (`edges_of` gives them and how many there were before the +/// cap), merged, sorted, one hit per destination, cut to the request's limit. +/// Hits score `conf` as a unit interval and cite the site through `detail`. +/// A cap that dropped edges makes the status `Partial`, with `noun` in the note. +pub fn ranked_hits( + descriptor: &MapDescriptor, + request: &MapRequest<'_>, + view: &SnapshotView<'_>, + cap: usize, + noun: &str, + edges_of: impl Fn(&NodeId) -> (Expanded, usize), + detail: impl Fn(Site) -> String, +) -> MapResponse { + let started = Instant::now(); + let mut edges = Vec::new(); + let mut dropped = 0; + for (seed, _) in request.seeds { + let (found, total) = edges_of(seed); + dropped += total - found.edges.len(); + edges.extend(found.edges); + } + cap_edges(&mut edges, usize::MAX); + let mut seen = HashSet::new(); + edges.retain(|edge| seen.insert(edge.dst.clone())); + edges.truncate(request.limit); + let mut response = MapResponse::empty(descriptor, view, MapStatus::Ok); + if dropped > 0 { + response.status = MapStatus::Partial { + note: Some(format!( + "{dropped} more {noun} than the cap of {cap} were not offered" + )), + warning: None, + }; + } + response.cost = Cost { + elapsed_ms: started.elapsed().as_secs_f64() * 1000.0, + nodes_touched: edges.len() as u64, + ..Cost::default() + }; + response.hits = edges + .into_iter() + .zip(1..) + .map(|(edge, rank)| MapHit { + node: edge.dst, + rank, + score: Some(f32::from(edge.conf) / f32::from(CONF_MAX)), + evidence: vec![Evidence { + via: Some(edge.src), + edge: edge.kind, + label: SourceLabel::Scip, + detail: edge.site.map(&detail), + }], + }) + .collect(); + response +} + /// The edges a seeded query's answer stands for, as [`crate::Map::expand`]'s /// default yields them: one per hit that is not the seed itself, witness from /// the hit's evidence label (the descriptor's first label when the hit has diff --git a/lci-maps/src/scip_impact.rs b/lci-maps/src/scip_impact.rs index 12a0a841..6d3dfea3 100644 --- a/lci-maps/src/scip_impact.rs +++ b/lci-maps/src/scip_impact.rs @@ -45,9 +45,11 @@ //! which reads the same file. use crate::{ - Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, Evidence, FreshnessModel, Map, - MapDescriptor, MapHit, MapId, MapRequest, MapResponse, MapStatus, SnapshotView, SourceLabel, - edge::{CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges}, + Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, FreshnessModel, Map, MapDescriptor, MapId, + MapRequest, MapResponse, SnapshotView, SourceLabel, + edge::{ + CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges, ranked_hits, + }, impact::ImpactIndex, live_grep::CostLog, }; @@ -319,51 +321,15 @@ impl Map for ImpactMap { } async fn query(&self, request: &MapRequest<'_>, view: &SnapshotView<'_>) -> MapResponse { - let started = Instant::now(); - let mut edges = Vec::new(); - let mut dropped = 0; - for (seed, _) in request.seeds { - let (found, total) = self.edges_of(seed); - dropped += total - found.edges.len(); - edges.extend(found.edges); - } - cap_edges(&mut edges, usize::MAX); - let mut seen = HashSet::new(); - edges.retain(|edge| seen.insert(edge.dst.clone())); - edges.truncate(request.limit); - let mut response = MapResponse::empty(&self.descriptor, view, MapStatus::Ok); - if dropped > 0 { - response.status = MapStatus::Partial { - note: Some(format!( - "{dropped} more users than the cap of {} were not offered", - self.options.cap - )), - warning: None, - }; - } - response.cost = Cost { - elapsed_ms: started.elapsed().as_secs_f64() * 1000.0, - nodes_touched: edges.len() as u64, - ..Cost::default() - }; - response.hits = edges - .into_iter() - .zip(1..) - .map(|(edge, rank)| MapHit { - node: edge.dst, - rank, - score: Some(f32::from(edge.conf) / f32::from(CONF_MAX)), - evidence: vec![Evidence { - via: Some(edge.src), - edge: edge.kind, - label: SourceLabel::Scip, - detail: edge - .site - .map(|s| format!("references the seed at {}:{}", s.path, s.line + 1)), - }], - }) - .collect(); - response + ranked_hits( + &self.descriptor, + request, + view, + self.options.cap, + "users", + |seed| self.edges_of(seed), + |s| format!("references the seed at {}:{}", s.path, s.line + 1), + ) } async fn expand(&self, node: &NodeId, _budget: Budget, _view: &SnapshotView<'_>) -> Expanded { diff --git a/lci-maps/src/scip_impact/tests.rs b/lci-maps/src/scip_impact/tests.rs index 50cadf68..ef3bf6ac 100644 --- a/lci-maps/src/scip_impact/tests.rs +++ b/lci-maps/src/scip_impact/tests.rs @@ -4,7 +4,7 @@ use super::*; use crate::{ - Registry, SnapshotRef, + MapStatus, Registry, SnapshotRef, audit::audit_edge_sources, kit_c3::check_expand, scip_graph::{Range, RawDocument, RawOccurrence}, diff --git a/lci-maps/src/scip_implements.rs b/lci-maps/src/scip_implements.rs index 8f5dc9ad..adbec345 100644 --- a/lci-maps/src/scip_implements.rs +++ b/lci-maps/src/scip_implements.rs @@ -87,9 +87,11 @@ //! `graph.scip`. use crate::{ - Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, Evidence, FreshnessModel, Map, - MapDescriptor, MapHit, MapId, MapRequest, MapResponse, MapStatus, SnapshotView, SourceLabel, - edge::{CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges}, + Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, FreshnessModel, Map, MapDescriptor, MapId, + MapRequest, MapResponse, SnapshotView, SourceLabel, + edge::{ + CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges, ranked_hits, + }, impact::chain_of, live_grep::CostLog, scip_graph::{GraphEdgeKind, ScipGraph, Witness}, @@ -447,51 +449,15 @@ impl Map for ImplementsMap { } async fn query(&self, request: &MapRequest<'_>, view: &SnapshotView<'_>) -> MapResponse { - let started = Instant::now(); - let mut edges = Vec::new(); - let mut dropped = 0; - for (seed, _) in request.seeds { - let (found, total) = self.edges_of(seed); - dropped += total - found.edges.len(); - edges.extend(found.edges); - } - cap_edges(&mut edges, usize::MAX); - let mut seen = HashSet::new(); - edges.retain(|edge| seen.insert(edge.dst.clone())); - edges.truncate(request.limit); - let mut response = MapResponse::empty(&self.descriptor, view, MapStatus::Ok); - if dropped > 0 { - response.status = MapStatus::Partial { - note: Some(format!( - "{dropped} more implementations than the cap of {} were not offered", - self.options.cap - )), - warning: None, - }; - } - response.cost = Cost { - elapsed_ms: started.elapsed().as_secs_f64() * 1000.0, - nodes_touched: edges.len() as u64, - ..Cost::default() - }; - response.hits = edges - .into_iter() - .zip(1..) - .map(|(edge, rank)| MapHit { - node: edge.dst, - rank, - score: Some(f32::from(edge.conf) / f32::from(CONF_MAX)), - evidence: vec![Evidence { - via: Some(edge.src), - edge: edge.kind, - label: SourceLabel::Scip, - detail: edge - .site - .map(|s| format!("implements at {}:{}", s.path, s.line + 1)), - }], - }) - .collect(); - response + ranked_hits( + &self.descriptor, + request, + view, + self.options.cap, + "implementations", + |seed| self.edges_of(seed), + |s| format!("implements at {}:{}", s.path, s.line + 1), + ) } async fn expand(&self, node: &NodeId, _budget: Budget, _view: &SnapshotView<'_>) -> Expanded { diff --git a/lci-maps/src/scip_implements/tests.rs b/lci-maps/src/scip_implements/tests.rs index f1f506fa..3edd6d6e 100644 --- a/lci-maps/src/scip_implements/tests.rs +++ b/lci-maps/src/scip_implements/tests.rs @@ -3,7 +3,7 @@ use super::*; use crate::{ - SnapshotRef, + MapStatus, SnapshotRef, kit_c3::check_expand, scip_graph::{DirSource, Range, RawDocument, RawOccurrence, SourceText}, }; From 443e4a82f7e9b41c80469499145c8bebb2779e49 Mon Sep 17 00:00:00 2001 From: Kadir Mert Okumus Date: Sat, 24 Oct 2026 02:59:42 +0300 Subject: [PATCH 2/3] Pin the SCIP map output; move TestedByMap onto the shared helper TestedByMap::query was a third copy of the ranking skeleton (cap EXPAND_LIMIT, noun "test units", detail "tests the seed at"). It now calls the shared function, renamed scip_ranked_hits since all three callers are SCIP maps; its doc says the cap is only reported. New pins per map, written and green before the move: the full Partial note, rank, score and the first evidence (via, edge, label, detail), plus a two-seed query that pins which edge survives the dedupe of a shared destination and that limit 1 truncates to the first hit. Evidence: cargo test -p lci-maps --lib 571 passed, 3 ignored, before and after the move; traverse_matrix 18/18; clippy --workspace --all-targets -D warnings clean; fmt clean. Co-Authored-By: Claude Opus 5.5 --- lci-maps/src/edge.rs | 6 +- lci-maps/src/scip_impact.rs | 5 +- lci-maps/src/scip_impact/tests.rs | 82 +++++++++++++++++++ lci-maps/src/scip_implements.rs | 5 +- lci-maps/src/scip_implements/tests.rs | 68 ++++++++++++++++ lci-maps/src/tested_by.rs | 63 ++++---------- lci-maps/src/tested_by/tests.rs | 113 +++++++++++++++++++++++++- 7 files changed, 287 insertions(+), 55 deletions(-) diff --git a/lci-maps/src/edge.rs b/lci-maps/src/edge.rs index 9538adb8..b08da3bf 100644 --- a/lci-maps/src/edge.rs +++ b/lci-maps/src/edge.rs @@ -241,8 +241,10 @@ pub fn cap_edges(edges: &mut Vec, cap: usize) { /// capped edges (`edges_of` gives them and how many there were before the /// cap), merged, sorted, one hit per destination, cut to the request's limit. /// Hits score `conf` as a unit interval and cite the site through `detail`. -/// A cap that dropped edges makes the status `Partial`, with `noun` in the note. -pub fn ranked_hits( +/// `cap` is only reported, not applied here (`edges_of` applies it): a cap +/// that dropped edges makes the status `Partial`, its note naming `noun` and +/// `cap`. +pub fn scip_ranked_hits( descriptor: &MapDescriptor, request: &MapRequest<'_>, view: &SnapshotView<'_>, diff --git a/lci-maps/src/scip_impact.rs b/lci-maps/src/scip_impact.rs index 6d3dfea3..4be16605 100644 --- a/lci-maps/src/scip_impact.rs +++ b/lci-maps/src/scip_impact.rs @@ -48,7 +48,8 @@ use crate::{ Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, FreshnessModel, Map, MapDescriptor, MapId, MapRequest, MapResponse, SnapshotView, SourceLabel, edge::{ - CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges, ranked_hits, + CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges, + scip_ranked_hits, }, impact::ImpactIndex, live_grep::CostLog, @@ -321,7 +322,7 @@ impl Map for ImpactMap { } async fn query(&self, request: &MapRequest<'_>, view: &SnapshotView<'_>) -> MapResponse { - ranked_hits( + scip_ranked_hits( &self.descriptor, request, view, diff --git a/lci-maps/src/scip_impact/tests.rs b/lci-maps/src/scip_impact/tests.rs index ef3bf6ac..71dd745f 100644 --- a/lci-maps/src/scip_impact/tests.rs +++ b/lci-maps/src/scip_impact/tests.rs @@ -543,3 +543,85 @@ async fn graph_scip_and_scip_impact_over_one_index_corroborate_once() { ); assert_eq!(reached.n_maps(), 1, "one index, one piece of evidence"); } + +// ---- pins: the seeded `query` output ------------------------------------------------- + +async fn ask(map: &ImpactMap, seeds: &[(NodeId, f32)], limit: usize) -> MapResponse { + let params = serde_json::Value::Null; + map.query( + &MapRequest { + text: None, + seeds, + limit, + budget: budget(), + params: ¶ms, + as_of: None, + }, + &view(), + ) + .await +} + +#[tokio::test] +async fn query_pins_the_partial_note_and_the_first_hit() { + let (ix, target) = crowd(100); + let map = ImpactMap::new(ix, &|_| None, ImpactOptions { cap: 5 }); + let node = map.node_of(&target).unwrap().clone(); + let response = ask(&map, &[(node.clone(), 1.0)], 100).await; + assert_eq!( + response.status, + MapStatus::Partial { + note: Some("95 more users than the cap of 5 were not offered".to_string()), + warning: None, + } + ); + assert_eq!(response.hits.len(), 5); + let hit = &response.hits[0]; + assert_eq!(hit.rank, 1); + assert_eq!(hit.score, Some(1.0)); + assert_eq!(response.hits[4].rank, 5); + assert_eq!(hit.evidence.len(), 1); + let evidence = &hit.evidence[0]; + assert_eq!(evidence.via, Some(node)); + assert_eq!(evidence.edge, EdgeKind::CallsOrRefs); + assert_eq!(evidence.label, crate::SourceLabel::Scip); + assert_eq!( + evidence.detail.as_deref(), + Some("references the seed at a.rs:2") + ); +} + +#[tokio::test] +async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { + let map = map(); + let foo = map.node_of(&sym("a/Foo#")).unwrap().clone(); + let run = method("run"); + // Both seeds reach `use_foo` (Foo at a.rs:3 col 15, run at a.rs:3 col 20); + // only Foo reaches the enum E. + let seeds = [(foo.clone(), 1.0), (run, 1.0)]; + let all = ask(&map, &seeds, 10).await; + assert_eq!(all.status, MapStatus::Ok); + let dsts: Vec<&NodeId> = all.hits.iter().map(|h| &h.node).collect(); + assert_eq!(dsts.len(), 2, "{dsts:?}"); + let use_foo = all + .hits + .iter() + .find(|h| h.node == function("use_foo")) + .expect("use_foo is offered once"); + // The first edge in sort order survives: the one with the smaller site. + assert_eq!(use_foo.evidence[0].via, Some(foo)); + assert_eq!( + use_foo.evidence[0].detail.as_deref(), + Some("references the seed at a.rs:3") + ); + assert_eq!( + all.hits.iter().map(|h| h.rank).collect::>(), + vec![1, 2] + ); + + let one = ask(&map, &seeds, 1).await; + assert_eq!(one.hits.len(), 1); + assert_eq!(one.hits[0].rank, 1); + assert_eq!(one.hits[0].node, all.hits[0].node); + assert_eq!(one.status, MapStatus::Ok); +} diff --git a/lci-maps/src/scip_implements.rs b/lci-maps/src/scip_implements.rs index adbec345..88124198 100644 --- a/lci-maps/src/scip_implements.rs +++ b/lci-maps/src/scip_implements.rs @@ -90,7 +90,8 @@ use crate::{ Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, FreshnessModel, Map, MapDescriptor, MapId, MapRequest, MapResponse, SnapshotView, SourceLabel, edge::{ - CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges, ranked_hits, + CONF_MAX, Edge, Expanded, MapCard, QuestionType, Site, WitnessClass, cap_edges, + scip_ranked_hits, }, impact::chain_of, live_grep::CostLog, @@ -449,7 +450,7 @@ impl Map for ImplementsMap { } async fn query(&self, request: &MapRequest<'_>, view: &SnapshotView<'_>) -> MapResponse { - ranked_hits( + scip_ranked_hits( &self.descriptor, request, view, diff --git a/lci-maps/src/scip_implements/tests.rs b/lci-maps/src/scip_implements/tests.rs index 3edd6d6e..77273b5b 100644 --- a/lci-maps/src/scip_implements/tests.rs +++ b/lci-maps/src/scip_implements/tests.rs @@ -289,6 +289,74 @@ fn the_card_is_dependent_and_good_for_impact_and_flow() { ); } +// ---- pins: the seeded `query` output ------------------------------------------------- + +async fn ask(map: &ImplementsMap, seeds: &[(NodeId, f32)], limit: usize) -> MapResponse { + let params = serde_json::Value::Null; + map.query( + &MapRequest { + text: None, + seeds, + limit, + budget: budget(), + params: ¶ms, + as_of: None, + }, + &view(), + ) + .await +} + +#[tokio::test] +async fn query_pins_the_partial_note_and_the_first_hit() { + let map = map(1); + let response = ask(&map, &[(tr(), 1.0)], 10).await; + assert_eq!( + response.status, + MapStatus::Partial { + note: Some("1 more implementations than the cap of 1 were not offered".to_string()), + warning: None, + } + ); + assert_eq!(response.hits.len(), 1); + let hit = &response.hits[0]; + assert_eq!(hit.rank, 1); + assert_eq!(hit.score, Some(1.0)); + assert_eq!(hit.node, a()); + assert_eq!(hit.evidence.len(), 1); + let evidence = &hit.evidence[0]; + assert_eq!(evidence.via, Some(tr())); + assert_eq!(evidence.edge, EdgeKind::Implements); + assert_eq!(evidence.label, crate::SourceLabel::Scip); + assert_eq!(evidence.detail.as_deref(), Some("implements at a.rs:6")); +} + +#[tokio::test] +async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { + let map = map(64); + // A and B both reach `Tr` (A at a.rs:6, B at a.rs:9): one hit, from A. + let both = ask(&map, &[(a(), 1.0), (b(), 1.0)], 10).await; + assert_eq!(both.status, MapStatus::Ok); + assert_eq!(both.hits.len(), 1, "{:?}", both.hits); + assert_eq!(both.hits[0].node, tr()); + assert_eq!(both.hits[0].rank, 1); + assert_eq!(both.hits[0].evidence[0].via, Some(a())); + assert_eq!( + both.hits[0].evidence[0].detail.as_deref(), + Some("implements at a.rs:6") + ); + + // With `Tr` as a third seed the edges to A and B join it: three hits, cut to one. + let seeds = [(a(), 1.0), (b(), 1.0), (tr(), 1.0)]; + let all = ask(&map, &seeds, 10).await; + assert_eq!(all.hits.len(), 3, "{:?}", all.hits); + let one = ask(&map, &seeds, 1).await; + assert_eq!(one.hits.len(), 1); + assert_eq!(one.hits[0].rank, 1); + assert_eq!(one.hits[0].node, all.hits[0].node); + assert_eq!(one.status, MapStatus::Ok); +} + #[test] fn without_source_the_map_has_no_edges() { let docs = vec![RawDocument { diff --git a/lci-maps/src/tested_by.rs b/lci-maps/src/tested_by.rs index 5293dc04..e3db3e4d 100644 --- a/lci-maps/src/tested_by.rs +++ b/lci-maps/src/tested_by.rs @@ -50,9 +50,11 @@ //! is [`FreshnessModel::PerFile`], like `scip.impact`. use crate::{ - Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, Evidence, FreshnessModel, Map, - MapDescriptor, MapHit, MapId, MapRequest, MapResponse, MapStatus, SnapshotView, SourceLabel, - edge::{CONF_MAX, Edge, Expanded, MapCard, QuestionType, WitnessClass, cap_edges}, + Budget, Contribution, Cost, EXPAND_LIMIT, EdgeKind, FreshnessModel, Map, MapDescriptor, MapId, + MapRequest, MapResponse, SnapshotView, SourceLabel, + edge::{ + CONF_MAX, Edge, Expanded, MapCard, QuestionType, WitnessClass, cap_edges, scip_ranked_hits, + }, impact::Chain, live_grep::CostLog, scip_impact::ImpactMap, @@ -61,7 +63,7 @@ use lci_core::{ filter::{SourceRole, classify_source_role}, node::{NodeId, NodeKind}, }; -use std::{collections::HashSet, sync::Arc, time::Instant}; +use std::{sync::Arc, time::Instant}; /// The id of [`TestedByMap`]. pub const TESTED_BY_ID: MapId = "tests.tested_by"; @@ -175,50 +177,15 @@ impl Map for TestedByMap { } async fn query(&self, request: &MapRequest<'_>, view: &SnapshotView<'_>) -> MapResponse { - let started = Instant::now(); - let mut edges = Vec::new(); - let mut dropped = 0; - for (seed, _) in request.seeds { - let (found, total) = self.edges_of(seed); - dropped += total - found.edges.len(); - edges.extend(found.edges); - } - cap_edges(&mut edges, usize::MAX); - let mut seen = HashSet::new(); - edges.retain(|edge| seen.insert(edge.dst.clone())); - edges.truncate(request.limit); - let mut response = MapResponse::empty(&self.descriptor, view, MapStatus::Ok); - if dropped > 0 { - response.status = MapStatus::Partial { - note: Some(format!( - "{dropped} more test units than the cap of {EXPAND_LIMIT} were not offered" - )), - warning: None, - }; - } - response.cost = Cost { - elapsed_ms: started.elapsed().as_secs_f64() * 1000.0, - nodes_touched: edges.len() as u64, - ..Cost::default() - }; - response.hits = edges - .into_iter() - .zip(1..) - .map(|(edge, rank)| MapHit { - node: edge.dst, - rank, - score: Some(f32::from(edge.conf) / f32::from(CONF_MAX)), - evidence: vec![Evidence { - via: Some(edge.src), - edge: edge.kind, - label: SourceLabel::Scip, - detail: edge - .site - .map(|s| format!("tests the seed at {}:{}", s.path, s.line + 1)), - }], - }) - .collect(); - response + scip_ranked_hits( + &self.descriptor, + request, + view, + EXPAND_LIMIT, + "test units", + |seed| self.edges_of(seed), + |s| format!("tests the seed at {}:{}", s.path, s.line + 1), + ) } async fn expand(&self, node: &NodeId, _budget: Budget, _view: &SnapshotView<'_>) -> Expanded { diff --git a/lci-maps/src/tested_by/tests.rs b/lci-maps/src/tested_by/tests.rs index d4b8daeb..ce517f01 100644 --- a/lci-maps/src/tested_by/tests.rs +++ b/lci-maps/src/tested_by/tests.rs @@ -3,7 +3,7 @@ use super::*; use crate::{ - SnapshotRef, + MapStatus, SnapshotRef, edge::sort_edges, impact::ImpactIndex, kit_c3::check_expand, @@ -288,6 +288,117 @@ async fn query_offers_one_hit_per_test_unit_with_scip_evidence() { ); } +// ---- pins: the seeded `query` output ------------------------------------------------- + +async fn ask(map: &TestedByMap, seeds: &[(NodeId, f32)], limit: usize) -> MapResponse { + let params = serde_json::Value::Null; + map.query( + &MapRequest { + text: None, + seeds, + limit, + budget: budget(), + params: ¶ms, + as_of: None, + }, + &view(), + ) + .await +} + +#[tokio::test] +async fn query_pins_the_partial_note_and_the_first_hit() { + let (ix, target) = crowd(EXPAND_LIMIT + 5); + let shared = impact(ix); + let map = TestedByMap::new(shared.clone()); + let node = shared.node_of(&target).unwrap().clone(); + let response = ask(&map, &[(node.clone(), 1.0)], 3).await; + assert_eq!( + response.status, + MapStatus::Partial { + note: Some("5 more test units than the cap of 256 were not offered".to_string()), + warning: None, + } + ); + assert_eq!(response.hits.len(), 3); + let hit = &response.hits[0]; + assert_eq!(hit.rank, 1); + assert_eq!(hit.score, Some(1.0)); + assert_eq!(response.hits[2].rank, 3); + assert_eq!(name_of(&hit.node), "t_000"); + assert_eq!(hit.evidence.len(), 1); + let evidence = &hit.evidence[0]; + assert_eq!(evidence.via, Some(node)); + assert_eq!(evidence.edge, EdgeKind::Tests); + assert_eq!(evidence.label, SourceLabel::Scip); + assert_eq!( + evidence.detail.as_deref(), + Some("tests the seed at tests/crowd.rs:1") + ); +} + +#[tokio::test] +async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { + let foo = sym("a/foo()."); + let bar = sym("a/bar()."); + let t_both = sym("a_test/t_both()."); + let t_foo = sym("a_test/t_foo()."); + let docs = vec![ + RawDocument { + path: "src/a.rs".into(), + occurrences: vec![ + occ((0, 3, 0, 6), &foo, 1, Some((0, 0, 0, 12))), + occ((1, 3, 1, 6), &bar, 1, Some((1, 0, 1, 12))), + ], + }, + RawDocument { + path: "tests/a_test.rs".into(), + occurrences: vec![ + occ((0, 3, 0, 9), &t_both, 1, Some((0, 0, 0, 30))), + occ((0, 12, 0, 15), &foo, 0, None), + occ((0, 20, 0, 23), &bar, 0, None), + occ((1, 3, 1, 9), &t_foo, 1, Some((1, 0, 1, 30))), + occ((1, 12, 1, 15), &foo, 0, None), + ], + }, + ]; + let kinds: HashMap = [foo.clone(), bar.clone(), t_both, t_foo] + .into_iter() + .map(|s| (s, Kind::Function)) + .collect(); + let shared = impact(ImpactIndex::build(docs, &kinds, None)); + let map = TestedByMap::new(shared.clone()); + let foo = shared.node_of(&foo).unwrap().clone(); + let bar = shared.node_of(&bar).unwrap().clone(); + // Both seeds reach t_both (foo at tests/a_test.rs:1 col 12, bar at col 20); + // only foo reaches t_foo. + let seeds = [(foo.clone(), 1.0), (bar, 1.0)]; + let all = ask(&map, &seeds, 10).await; + assert_eq!(all.status, MapStatus::Ok); + assert_eq!(all.hits.len(), 2, "{:?}", all.hits); + assert_eq!( + all.hits.iter().map(|h| h.rank).collect::>(), + vec![1, 2] + ); + let both = all + .hits + .iter() + .find(|h| name_of(&h.node) == "t_both") + .expect("t_both is offered once"); + // The first edge in sort order survives: the one with the smaller site. + assert_eq!(both.evidence[0].via, Some(foo)); + assert_eq!( + both.evidence[0].detail.as_deref(), + Some("tests the seed at tests/a_test.rs:1") + ); + + let one = ask(&map, &seeds, 1).await; + assert_eq!(one.hits.len(), 1); + assert_eq!(one.hits[0].rank, 1); + assert_eq!(one.hits[0].node, all.hits[0].node); + assert_eq!(one.status, MapStatus::Ok); +} + #[test] fn the_two_rules_and_their_limits() { let chain = |names: &[&str]| Chain { From 5c966f7707933e22eeb95b127cb09a4eee75e54a Mon Sep 17 00:00:00 2001 From: Kadir Mert Okumus Date: Sat, 24 Oct 2026 03:06:46 +0300 Subject: [PATCH 3/3] Pin the dedupe winner against seed order and the hit order as literals The winning seed is now listed second, so the dedupe tests fail if the global re-sort by site goes; the limit-10 hit order and the limit-1 hit are literals instead of self-comparisons. Evidence: cargo test -p lci-maps --lib 571 passed, 3 ignored; fmt and clippy -p lci-maps --all-targets -D warnings clean. Co-Authored-By: Claude Opus 5.5 --- lci-maps/src/scip_impact/tests.rs | 17 +++++++++++++---- lci-maps/src/scip_implements/tests.rs | 11 +++++++---- lci-maps/src/tested_by/tests.rs | 10 ++++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lci-maps/src/scip_impact/tests.rs b/lci-maps/src/scip_impact/tests.rs index 71dd745f..be3e2868 100644 --- a/lci-maps/src/scip_impact/tests.rs +++ b/lci-maps/src/scip_impact/tests.rs @@ -597,12 +597,21 @@ async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { let foo = map.node_of(&sym("a/Foo#")).unwrap().clone(); let run = method("run"); // Both seeds reach `use_foo` (Foo at a.rs:3 col 15, run at a.rs:3 col 20); - // only Foo reaches the enum E. - let seeds = [(foo.clone(), 1.0), (run, 1.0)]; + // only Foo reaches the enum E. The seed whose edge wins the dedupe is listed + // second, so only the global sort by site keeps it. + let seeds = [(run, 1.0), (foo.clone(), 1.0)]; + let enum_e = NodeId::Symbol(SymbolKey { + language: "rust".into(), + module_path: rust_module_path("a.rs"), + container: Vec::new(), + name: "E".into(), + kind: SymbolKind::Enum, + disambiguator: 0, + }); let all = ask(&map, &seeds, 10).await; assert_eq!(all.status, MapStatus::Ok); let dsts: Vec<&NodeId> = all.hits.iter().map(|h| &h.node).collect(); - assert_eq!(dsts.len(), 2, "{dsts:?}"); + assert_eq!(dsts, vec![&enum_e, &function("use_foo")]); let use_foo = all .hits .iter() @@ -622,6 +631,6 @@ async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { let one = ask(&map, &seeds, 1).await; assert_eq!(one.hits.len(), 1); assert_eq!(one.hits[0].rank, 1); - assert_eq!(one.hits[0].node, all.hits[0].node); + assert_eq!(one.hits[0].node, enum_e); assert_eq!(one.status, MapStatus::Ok); } diff --git a/lci-maps/src/scip_implements/tests.rs b/lci-maps/src/scip_implements/tests.rs index 77273b5b..676dc038 100644 --- a/lci-maps/src/scip_implements/tests.rs +++ b/lci-maps/src/scip_implements/tests.rs @@ -335,7 +335,9 @@ async fn query_pins_the_partial_note_and_the_first_hit() { async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { let map = map(64); // A and B both reach `Tr` (A at a.rs:6, B at a.rs:9): one hit, from A. - let both = ask(&map, &[(a(), 1.0), (b(), 1.0)], 10).await; + // The seed whose edge wins is listed second, so only the global sort by + // site keeps it. + let both = ask(&map, &[(b(), 1.0), (a(), 1.0)], 10).await; assert_eq!(both.status, MapStatus::Ok); assert_eq!(both.hits.len(), 1, "{:?}", both.hits); assert_eq!(both.hits[0].node, tr()); @@ -347,13 +349,14 @@ async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { ); // With `Tr` as a third seed the edges to A and B join it: three hits, cut to one. - let seeds = [(a(), 1.0), (b(), 1.0), (tr(), 1.0)]; + let seeds = [(b(), 1.0), (a(), 1.0), (tr(), 1.0)]; let all = ask(&map, &seeds, 10).await; - assert_eq!(all.hits.len(), 3, "{:?}", all.hits); + let nodes: Vec<&NodeId> = all.hits.iter().map(|h| &h.node).collect(); + assert_eq!(nodes, vec![&a(), &b(), &tr()]); let one = ask(&map, &seeds, 1).await; assert_eq!(one.hits.len(), 1); assert_eq!(one.hits[0].rank, 1); - assert_eq!(one.hits[0].node, all.hits[0].node); + assert_eq!(one.hits[0].node, a()); assert_eq!(one.status, MapStatus::Ok); } diff --git a/lci-maps/src/tested_by/tests.rs b/lci-maps/src/tested_by/tests.rs index ce517f01..8f5accad 100644 --- a/lci-maps/src/tested_by/tests.rs +++ b/lci-maps/src/tested_by/tests.rs @@ -371,11 +371,13 @@ async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { let foo = shared.node_of(&foo).unwrap().clone(); let bar = shared.node_of(&bar).unwrap().clone(); // Both seeds reach t_both (foo at tests/a_test.rs:1 col 12, bar at col 20); - // only foo reaches t_foo. - let seeds = [(foo.clone(), 1.0), (bar, 1.0)]; + // only foo reaches t_foo. The seed whose edge wins the dedupe is listed + // second, so only the global sort by site keeps it. + let seeds = [(bar, 1.0), (foo.clone(), 1.0)]; let all = ask(&map, &seeds, 10).await; assert_eq!(all.status, MapStatus::Ok); - assert_eq!(all.hits.len(), 2, "{:?}", all.hits); + let hit_names: Vec<&str> = all.hits.iter().map(|h| name_of(&h.node)).collect(); + assert_eq!(hit_names, ["t_both", "t_foo"]); assert_eq!( all.hits.iter().map(|h| h.rank).collect::>(), vec![1, 2] @@ -395,7 +397,7 @@ async fn query_dedupes_a_shared_destination_and_truncates_to_the_limit() { let one = ask(&map, &seeds, 1).await; assert_eq!(one.hits.len(), 1); assert_eq!(one.hits[0].rank, 1); - assert_eq!(one.hits[0].node, all.hits[0].node); + assert_eq!(name_of(&one.hits[0].node), "t_both"); assert_eq!(one.status, MapStatus::Ok); }